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 297
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]; \
2577     cimg_vsnprintf(_message,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)((unsigned int)-1>>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 unsigned long format(const cimg_uint64 val) { return (unsigned long)val; }
2858     };
2859 
2860     template<> struct type<cimg_int64> {
2861       static const char* string() { static const char *const s = "int64"; return s; }
2862       static bool is_float() { return false; }
2863       static bool is_inf(const cimg_int64) { return false; }
2864       static bool is_nan(const cimg_int64) { return false; }
2865       static bool is_finite(const cimg_int64) { return true; }
2866       static cimg_int64 min() { return ~max(); }
2867       static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); }
2868       static cimg_int64 inf() { return max(); }
2869       static cimg_int64 cut(const double val) {
2870         return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
2871       }
2872       static const char* format() { return cimg_fint64; }
2873       static const char* format_s() { return cimg_fint64; }
2874       static long format(const long val) { return (long)val; }
2875     };
2876 
2877     template<> struct type<double> {
2878       static const char* string() { static const char *const s = "double"; return s; }
2879       static bool is_float() { return true; }
2880       static bool is_inf(const double val) {
2881 #ifdef isinf
2882         return (bool)isinf(val);
2883 #else
2884         return !is_nan(val) && (val<cimg::type<double>::min() || val>cimg::type<double>::max());
2885 #endif
2886       }
2887       static bool is_nan(const double val) { // Custom version that works with '-ffast-math'
2888         if (sizeof(double)==8) {
2889           cimg_uint64 u;
2890           std::memcpy(&u,&val,sizeof(double));
2891           return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000;
2892         }
2893 #ifdef isnan
2894         return (bool)isnan(val);
2895 #else
2896         return !(val==val);
2897 #endif
2898       }
2899       static bool is_finite(const double val) {
2900 #ifdef isfinite
2901         return (bool)isfinite(val);
2902 #else
2903         return !is_nan(val) && !is_inf(val);
2904 #endif
2905       }
2906       static double min() { return -DBL_MAX; }
2907       static double max() { return DBL_MAX; }
2908       static double inf() {
2909 #ifdef INFINITY
2910         return (double)INFINITY;
2911 #else
2912         return max()*max();
2913 #endif
2914       }
2915       static double nan() {
2916 #ifdef NAN
2917         return (double)NAN;
2918 #else
2919         const double val_nan = -std::sqrt(-1.); return val_nan;
2920 #endif
2921       }
2922       static double cut(const double val) { return val; }
2923       static const char* format() { return "%.17g"; }
2924       static const char* format_s() { return "%g"; }
2925       static double format(const double val) { return val; }
2926     };
2927 
2928     template<> struct type<float> {
2929       static const char* string() { static const char *const s = "float"; return s; }
2930       static bool is_float() { return true; }
2931       static bool is_inf(const float val) {
2932 #ifdef isinf
2933         return (bool)isinf(val);
2934 #else
2935         return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max());
2936 #endif
2937       }
2938       static bool is_nan(const float val) { // Custom version that works with '-ffast-math'
2939         if (sizeof(float)==4) {
2940           unsigned int u;
2941           std::memcpy(&u,&val,sizeof(float));
2942           return (u&0x7fffffff)>0x7f800000;
2943         }
2944 #ifdef isnan
2945         return (bool)isnan(val);
2946 #else
2947         return !(val==val);
2948 #endif
2949       }
2950       static bool is_finite(const float val) {
2951 #ifdef isfinite
2952         return (bool)isfinite(val);
2953 #else
2954         return !is_nan(val) && !is_inf(val);
2955 #endif
2956       }
2957       static float min() { return -FLT_MAX; }
2958       static float max() { return FLT_MAX; }
2959       static float inf() { return (float)cimg::type<double>::inf(); }
2960       static float nan() { return (float)cimg::type<double>::nan(); }
2961       static float cut(const double val) { return (float)val; }
2962       static float cut(const float val) { return (float)val; }
2963       static const char* format() { return "%.9g"; }
2964       static const char* format_s() { return "%g"; }
2965       static double format(const float val) { return (double)val; }
2966     };
2967 
2968     template<> struct type<long double> {
2969       static const char* string() { static const char *const s = "long double"; return s; }
2970       static bool is_float() { return true; }
2971       static bool is_inf(const long double val) {
2972 #ifdef isinf
2973         return (bool)isinf(val);
2974 #else
2975         return !is_nan(val) && (val<cimg::type<long double>::min() || val>cimg::type<long double>::max());
2976 #endif
2977       }
2978       static bool is_nan(const long double val) {
2979 #ifdef isnan
2980         return (bool)isnan(val);
2981 #else
2982         return !(val==val);
2983 #endif
2984       }
2985       static bool is_finite(const long double val) {
2986 #ifdef isfinite
2987         return (bool)isfinite(val);
2988 #else
2989         return !is_nan(val) && !is_inf(val);
2990 #endif
2991       }
2992       static long double min() { return -LDBL_MAX; }
2993       static long double max() { return LDBL_MAX; }
2994       static long double inf() { return max()*max(); }
2995       static long double nan() { const long double val_nan = -std::sqrt(-1.L); return val_nan; }
2996       static long double cut(const long double val) { return val; }
2997       static const char* format() { return "%.17g"; }
2998       static const char* format_s() { return "%g"; }
2999       static double format(const long double val) { return (double)val; }
3000     };
3001 
3002 #ifdef cimg_use_half
3003     template<> struct type<half> {
3004       static const char* string() { static const char *const s = "half"; return s; }
3005       static bool is_float() { return true; }
3006       static bool is_inf(const long double val) {
3007 #ifdef isinf
3008         return (bool)isinf(val);
3009 #else
3010         return !is_nan(val) && (val<cimg::type<half>::min() || val>cimg::type<half>::max());
3011 #endif
3012       }
3013       static bool is_nan(const half val) { // Custom version that works with '-ffast-math'
3014         if (sizeof(half)==2) {
3015           short u;
3016           std::memcpy(&u,&val,sizeof(short));
3017           return (bool)((u&0x7fff)>0x7c00);
3018         }
3019         return cimg::type<float>::is_nan((float)val);
3020       }
3021       static bool is_finite(const half val) {
3022 #ifdef isfinite
3023         return (bool)isfinite(val);
3024 #else
3025         return !is_nan(val) && !is_inf(val);
3026 #endif
3027       }
3028       static half min() { return (half)-65504; }
3029       static half max() { return (half)65504; }
3030       static half inf() { return max()*max(); }
3031       static half nan() { const half val_nan = (half)-std::sqrt(-1.); return val_nan; }
3032       static half cut(const double val) { return (half)val; }
3033       static const char* format() { return "%.9g"; }
3034       static const char* format_s() { return "%g"; }
3035       static double format(const half val) { return (double)val; }
3036     };
3037 #endif
3038 
3039     template<typename T, typename t> struct superset { typedef T type; };
3040     template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
3041     template<> struct superset<bool,char> { typedef char type; };
3042     template<> struct superset<bool,signed char> { typedef signed char type; };
3043     template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
3044     template<> struct superset<bool,short> { typedef short type; };
3045     template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
3046     template<> struct superset<bool,int> { typedef int type; };
3047     template<> struct superset<bool,cimg_uint64> { typedef cimg_uint64 type; };
3048     template<> struct superset<bool,cimg_int64> { typedef cimg_int64 type; };
3049     template<> struct superset<bool,float> { typedef float type; };
3050     template<> struct superset<bool,double> { typedef double type; };
3051     template<> struct superset<unsigned char,char> { typedef short type; };
3052     template<> struct superset<unsigned char,signed char> { typedef short type; };
3053     template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
3054     template<> struct superset<unsigned char,short> { typedef short type; };
3055     template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
3056     template<> struct superset<unsigned char,int> { typedef int type; };
3057     template<> struct superset<unsigned char,cimg_uint64> { typedef cimg_uint64 type; };
3058     template<> struct superset<unsigned char,cimg_int64> { typedef cimg_int64 type; };
3059     template<> struct superset<unsigned char,float> { typedef float type; };
3060     template<> struct superset<unsigned char,double> { typedef double type; };
3061     template<> struct superset<signed char,unsigned char> { typedef short type; };
3062     template<> struct superset<signed char,char> { typedef short type; };
3063     template<> struct superset<signed char,unsigned short> { typedef int type; };
3064     template<> struct superset<signed char,short> { typedef short type; };
3065     template<> struct superset<signed char,unsigned int> { typedef cimg_int64 type; };
3066     template<> struct superset<signed char,int> { typedef int type; };
3067     template<> struct superset<signed char,cimg_uint64> { typedef cimg_int64 type; };
3068     template<> struct superset<signed char,cimg_int64> { typedef cimg_int64 type; };
3069     template<> struct superset<signed char,float> { typedef float type; };
3070     template<> struct superset<signed char,double> { typedef double type; };
3071     template<> struct superset<char,unsigned char> { typedef short type; };
3072     template<> struct superset<char,signed char> { typedef short type; };
3073     template<> struct superset<char,unsigned short> { typedef int type; };
3074     template<> struct superset<char,short> { typedef short type; };
3075     template<> struct superset<char,unsigned int> { typedef cimg_int64 type; };
3076     template<> struct superset<char,int> { typedef int type; };
3077     template<> struct superset<char,cimg_uint64> { typedef cimg_int64 type; };
3078     template<> struct superset<char,cimg_int64> { typedef cimg_int64 type; };
3079     template<> struct superset<char,float> { typedef float type; };
3080     template<> struct superset<char,double> { typedef double type; };
3081     template<> struct superset<unsigned short,char> { typedef int type; };
3082     template<> struct superset<unsigned short,signed char> { typedef int type; };
3083     template<> struct superset<unsigned short,short> { typedef int type; };
3084     template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
3085     template<> struct superset<unsigned short,int> { typedef int type; };
3086     template<> struct superset<unsigned short,cimg_uint64> { typedef cimg_uint64 type; };
3087     template<> struct superset<unsigned short,cimg_int64> { typedef cimg_int64 type; };
3088     template<> struct superset<unsigned short,float> { typedef float type; };
3089     template<> struct superset<unsigned short,double> { typedef double type; };
3090     template<> struct superset<short,unsigned short> { typedef int type; };
3091     template<> struct superset<short,unsigned int> { typedef cimg_int64 type; };
3092     template<> struct superset<short,int> { typedef int type; };
3093     template<> struct superset<short,cimg_uint64> { typedef cimg_int64 type; };
3094     template<> struct superset<short,cimg_int64> { typedef cimg_int64 type; };
3095     template<> struct superset<short,float> { typedef float type; };
3096     template<> struct superset<short,double> { typedef double type; };
3097     template<> struct superset<unsigned int,char> { typedef cimg_int64 type; };
3098     template<> struct superset<unsigned int,signed char> { typedef cimg_int64 type; };
3099     template<> struct superset<unsigned int,short> { typedef cimg_int64 type; };
3100     template<> struct superset<unsigned int,int> { typedef cimg_int64 type; };
3101     template<> struct superset<unsigned int,cimg_uint64> { typedef cimg_uint64 type; };
3102     template<> struct superset<unsigned int,cimg_int64> { typedef cimg_int64 type; };
3103     template<> struct superset<unsigned int,float> { typedef float type; };
3104     template<> struct superset<unsigned int,double> { typedef double type; };
3105     template<> struct superset<int,unsigned int> { typedef cimg_int64 type; };
3106     template<> struct superset<int,cimg_uint64> { typedef cimg_int64 type; };
3107     template<> struct superset<int,cimg_int64> { typedef cimg_int64 type; };
3108     template<> struct superset<int,float> { typedef float type; };
3109     template<> struct superset<int,double> { typedef double type; };
3110     template<> struct superset<cimg_uint64,char> { typedef cimg_int64 type; };
3111     template<> struct superset<cimg_uint64,signed char> { typedef cimg_int64 type; };
3112     template<> struct superset<cimg_uint64,short> { typedef cimg_int64 type; };
3113     template<> struct superset<cimg_uint64,int> { typedef cimg_int64 type; };
3114     template<> struct superset<cimg_uint64,cimg_int64> { typedef cimg_int64 type; };
3115     template<> struct superset<cimg_uint64,float> { typedef double type; };
3116     template<> struct superset<cimg_uint64,double> { typedef double type; };
3117     template<> struct superset<cimg_int64,float> { typedef double type; };
3118     template<> struct superset<cimg_int64,double> { typedef double type; };
3119     template<> struct superset<float,cimg_uint64> { typedef double type; };
3120     template<> struct superset<float,cimg_int64> { typedef double type; };
3121     template<> struct superset<float,double> { typedef double type; };
3122 
3123 #ifdef cimg_use_half
3124     template<> struct superset<half,unsigned short> { typedef float type; };
3125     template<> struct superset<half,short> { typedef float type; };
3126     template<> struct superset<half,unsigned int> { typedef float type; };
3127     template<> struct superset<half,int> { typedef float type; };
3128     template<> struct superset<half,cimg_uint64> { typedef float type; };
3129     template<> struct superset<half,cimg_int64> { typedef float type; };
3130     template<> struct superset<half,float> { typedef float type; };
3131     template<> struct superset<half,double> { typedef double type; };
3132 #endif
3133 
3134     template<typename t1, typename t2, typename t3> struct superset2 {
3135       typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
3136     };
3137 
3138     template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
3139       typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
3140     };
3141 
3142     template<typename t1, typename t2> struct last { typedef t2 type; };
3143 
3144 #define _cimg_Tt typename cimg::superset<T,t>::type
3145 #define _cimg_Tfloat typename cimg::superset<T,float>::type
3146 #define _cimg_tfloat typename cimg::superset<t,float>::type
3147 #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
3148 #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
3149 
3150     // Define variables used internally by CImg.
3151 #if cimg_display==1
3152     struct X11_static {
3153       unsigned int nb_wins;
3154       pthread_t *events_thread;
3155       pthread_cond_t wait_event;
3156       pthread_mutex_t wait_event_mutex;
3157       CImgDisplay **wins;
3158       Display *display;
3159       unsigned int nb_bits;
3160       bool is_blue_first;
3161       bool is_shm_enabled;
3162       bool byte_order;
3163 
3164 #ifdef cimg_use_xrandr
3165       XRRScreenSize *resolutions;
3166       Rotation curr_rotation;
3167       unsigned int curr_resolution;
3168       unsigned int nb_resolutions;
3169 #endif
3170       X11_static():nb_wins(0),events_thread(0),display(0),
3171                  nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
3172 #ifdef __FreeBSD__
3173         XInitThreads();
3174 #endif
3175         wins = new CImgDisplay*[1024];
3176         pthread_mutex_init(&wait_event_mutex,0);
3177         pthread_cond_init(&wait_event,0);
3178 
3179 #ifdef cimg_use_xrandr
3180         resolutions = 0;
3181         curr_rotation = 0;
3182         curr_resolution = nb_resolutions = 0;
3183 #endif
3184       }
3185 
3186       ~X11_static() {
3187         delete[] wins;
3188         /*
3189           if (events_thread) {
3190           pthread_cancel(*events_thread);
3191           delete events_thread;
3192           }
3193           if (display) { } // XCloseDisplay(display); }
3194           pthread_cond_destroy(&wait_event);
3195           pthread_mutex_unlock(&wait_event_mutex);
3196           pthread_mutex_destroy(&wait_event_mutex);
3197         */
3198       }
3199     }; // struct X11_static { ...
3200 #if defined(cimg_module)
3201     X11_static& X11_attr();
3202 #elif defined(cimg_main)
3203     X11_static& X11_attr() { static X11_static val; return val; }
3204 #else
3205     inline X11_static& X11_attr() { static X11_static val; return val; }
3206 #endif
3207 
3208 #elif cimg_display==2
3209     struct Win32_static {
3210       HANDLE wait_event;
3211       Win32_static() { wait_event = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); }
3212     }; // struct Win32_static { ...
3213 #if defined(cimg_module)
3214     Win32_static& Win32_attr();
3215 #elif defined(cimg_main)
3216     Win32_static& Win32_attr() { static Win32_static val; return val; }
3217 #else
3218     inline Win32_static& Win32_attr() { static Win32_static val; return val; }
3219 #endif
3220 #endif
3221 #define cimg_lock_display() cimg::mutex(15)
3222 #define cimg_unlock_display() cimg::mutex(15,0)
3223 
3224     struct Mutex_static {
3225 #if cimg_OS==1 && (defined(cimg_use_pthread) || cimg_display==1)
3226       pthread_mutex_t mutex[32];
3227       Mutex_static() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); }
3228       void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); }
3229       void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); }
3230       int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); }
3231 #elif cimg_OS==2
3232       HANDLE mutex[32];
3233       Mutex_static() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE_WIN,0); }
3234       void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); }
3235       void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); }
3236       int trylock(const unsigned int) { return 0; }
3237 #else
3238       Mutex_static() {}
3239       void lock(const unsigned int) {}
3240       void unlock(const unsigned int) {}
3241       int trylock(const unsigned int) { return 0; }
3242 #endif
3243     }; // struct Mutex_static { ...
3244 #if defined(cimg_module)
3245     Mutex_static& Mutex_attr();
3246 #elif defined(cimg_main)
3247     Mutex_static& Mutex_attr() { static Mutex_static val; return val; }
3248 #else
3249     inline Mutex_static& Mutex_attr() { static Mutex_static val; return val; }
3250 #endif
3251 
3252 #if defined(cimg_use_magick)
3253     struct Magick_static {
3254       Magick_static() {
3255         Magick::InitializeMagick("");
3256       }
3257     }; // struct Magick_static { ...
3258     static Magick_static _Magick_static;
3259 #endif
3260 
3261 #if defined(cimg_use_fftw3) && !defined(cimg_use_fftw3_singlethread)
3262     struct FFTW3_static {
3263       FFTW3_static() {
3264         fftw_init_threads();
3265       }
3266     }; // struct FFTW3_static { ...
3267     static FFTW3_static _FFTW3_static;
3268 #endif
3269 
3270 #if cimg_display==1
3271     // Define keycodes for X11-based graphical systems.
3272     const unsigned int keyESC        = XK_Escape;
3273     const unsigned int keyF1         = XK_F1;
3274     const unsigned int keyF2         = XK_F2;
3275     const unsigned int keyF3         = XK_F3;
3276     const unsigned int keyF4         = XK_F4;
3277     const unsigned int keyF5         = XK_F5;
3278     const unsigned int keyF6         = XK_F6;
3279     const unsigned int keyF7         = XK_F7;
3280     const unsigned int keyF8         = XK_F8;
3281     const unsigned int keyF9         = XK_F9;
3282     const unsigned int keyF10        = XK_F10;
3283     const unsigned int keyF11        = XK_F11;
3284     const unsigned int keyF12        = XK_F12;
3285     const unsigned int keyPAUSE      = XK_Pause;
3286     const unsigned int key1          = XK_1;
3287     const unsigned int key2          = XK_2;
3288     const unsigned int key3          = XK_3;
3289     const unsigned int key4          = XK_4;
3290     const unsigned int key5          = XK_5;
3291     const unsigned int key6          = XK_6;
3292     const unsigned int key7          = XK_7;
3293     const unsigned int key8          = XK_8;
3294     const unsigned int key9          = XK_9;
3295     const unsigned int key0          = XK_0;
3296     const unsigned int keyBACKSPACE  = XK_BackSpace;
3297     const unsigned int keyINSERT     = XK_Insert;
3298     const unsigned int keyHOME       = XK_Home;
3299     const unsigned int keyPAGEUP     = XK_Page_Up;
3300     const unsigned int keyTAB        = XK_Tab;
3301     const unsigned int keyQ          = XK_q;
3302     const unsigned int keyW          = XK_w;
3303     const unsigned int keyE          = XK_e;
3304     const unsigned int keyR          = XK_r;
3305     const unsigned int keyT          = XK_t;
3306     const unsigned int keyY          = XK_y;
3307     const unsigned int keyU          = XK_u;
3308     const unsigned int keyI          = XK_i;
3309     const unsigned int keyO          = XK_o;
3310     const unsigned int keyP          = XK_p;
3311     const unsigned int keyDELETE     = XK_Delete;
3312     const unsigned int keyEND        = XK_End;
3313     const unsigned int keyPAGEDOWN   = XK_Page_Down;
3314     const unsigned int keyCAPSLOCK   = XK_Caps_Lock;
3315     const unsigned int keyA          = XK_a;
3316     const unsigned int keyS          = XK_s;
3317     const unsigned int keyD          = XK_d;
3318     const unsigned int keyF          = XK_f;
3319     const unsigned int keyG          = XK_g;
3320     const unsigned int keyH          = XK_h;
3321     const unsigned int keyJ          = XK_j;
3322     const unsigned int keyK          = XK_k;
3323     const unsigned int keyL          = XK_l;
3324     const unsigned int keyENTER      = XK_Return;
3325     const unsigned int keySHIFTLEFT  = XK_Shift_L;
3326     const unsigned int keyZ          = XK_z;
3327     const unsigned int keyX          = XK_x;
3328     const unsigned int keyC          = XK_c;
3329     const unsigned int keyV          = XK_v;
3330     const unsigned int keyB          = XK_b;
3331     const unsigned int keyN          = XK_n;
3332     const unsigned int keyM          = XK_m;
3333     const unsigned int keySHIFTRIGHT = XK_Shift_R;
3334     const unsigned int keyARROWUP    = XK_Up;
3335     const unsigned int keyCTRLLEFT   = XK_Control_L;
3336     const unsigned int keyAPPLEFT    = XK_Super_L;
3337     const unsigned int keyALT        = XK_Alt_L;
3338     const unsigned int keySPACE      = XK_space;
3339     const unsigned int keyALTGR      = XK_Alt_R;
3340     const unsigned int keyAPPRIGHT   = XK_Super_R;
3341     const unsigned int keyMENU       = XK_Menu;
3342     const unsigned int keyCTRLRIGHT  = XK_Control_R;
3343     const unsigned int keyARROWLEFT  = XK_Left;
3344     const unsigned int keyARROWDOWN  = XK_Down;
3345     const unsigned int keyARROWRIGHT = XK_Right;
3346     const unsigned int keyPAD0       = XK_KP_0;
3347     const unsigned int keyPAD1       = XK_KP_1;
3348     const unsigned int keyPAD2       = XK_KP_2;
3349     const unsigned int keyPAD3       = XK_KP_3;
3350     const unsigned int keyPAD4       = XK_KP_4;
3351     const unsigned int keyPAD5       = XK_KP_5;
3352     const unsigned int keyPAD6       = XK_KP_6;
3353     const unsigned int keyPAD7       = XK_KP_7;
3354     const unsigned int keyPAD8       = XK_KP_8;
3355     const unsigned int keyPAD9       = XK_KP_9;
3356     const unsigned int keyPADADD     = XK_KP_Add;
3357     const unsigned int keyPADSUB     = XK_KP_Subtract;
3358     const unsigned int keyPADMUL     = XK_KP_Multiply;
3359     const unsigned int keyPADDIV     = XK_KP_Divide;
3360 
3361 #elif cimg_display==2
3362     // Define keycodes for Windows.
3363     const unsigned int keyESC        = VK_ESCAPE;
3364     const unsigned int keyF1         = VK_F1;
3365     const unsigned int keyF2         = VK_F2;
3366     const unsigned int keyF3         = VK_F3;
3367     const unsigned int keyF4         = VK_F4;
3368     const unsigned int keyF5         = VK_F5;
3369     const unsigned int keyF6         = VK_F6;
3370     const unsigned int keyF7         = VK_F7;
3371     const unsigned int keyF8         = VK_F8;
3372     const unsigned int keyF9         = VK_F9;
3373     const unsigned int keyF10        = VK_F10;
3374     const unsigned int keyF11        = VK_F11;
3375     const unsigned int keyF12        = VK_F12;
3376     const unsigned int keyPAUSE      = VK_PAUSE;
3377     const unsigned int key1          = '1';
3378     const unsigned int key2          = '2';
3379     const unsigned int key3          = '3';
3380     const unsigned int key4          = '4';
3381     const unsigned int key5          = '5';
3382     const unsigned int key6          = '6';
3383     const unsigned int key7          = '7';
3384     const unsigned int key8          = '8';
3385     const unsigned int key9          = '9';
3386     const unsigned int key0          = '0';
3387     const unsigned int keyBACKSPACE  = VK_BACK;
3388     const unsigned int keyINSERT     = VK_INSERT;
3389     const unsigned int keyHOME       = VK_HOME;
3390     const unsigned int keyPAGEUP     = VK_PRIOR;
3391     const unsigned int keyTAB        = VK_TAB;
3392     const unsigned int keyQ          = 'Q';
3393     const unsigned int keyW          = 'W';
3394     const unsigned int keyE          = 'E';
3395     const unsigned int keyR          = 'R';
3396     const unsigned int keyT          = 'T';
3397     const unsigned int keyY          = 'Y';
3398     const unsigned int keyU          = 'U';
3399     const unsigned int keyI          = 'I';
3400     const unsigned int keyO          = 'O';
3401     const unsigned int keyP          = 'P';
3402     const unsigned int keyDELETE     = VK_DELETE;
3403     const unsigned int keyEND        = VK_END;
3404     const unsigned int keyPAGEDOWN   = VK_NEXT;
3405     const unsigned int keyCAPSLOCK   = VK_CAPITAL;
3406     const unsigned int keyA          = 'A';
3407     const unsigned int keyS          = 'S';
3408     const unsigned int keyD          = 'D';
3409     const unsigned int keyF          = 'F';
3410     const unsigned int keyG          = 'G';
3411     const unsigned int keyH          = 'H';
3412     const unsigned int keyJ          = 'J';
3413     const unsigned int keyK          = 'K';
3414     const unsigned int keyL          = 'L';
3415     const unsigned int keyENTER      = VK_RETURN;
3416     const unsigned int keySHIFTLEFT  = VK_SHIFT;
3417     const unsigned int keyZ          = 'Z';
3418     const unsigned int keyX          = 'X';
3419     const unsigned int keyC          = 'C';
3420     const unsigned int keyV          = 'V';
3421     const unsigned int keyB          = 'B';
3422     const unsigned int keyN          = 'N';
3423     const unsigned int keyM          = 'M';
3424     const unsigned int keySHIFTRIGHT = VK_SHIFT;
3425     const unsigned int keyARROWUP    = VK_UP;
3426     const unsigned int keyCTRLLEFT   = VK_CONTROL;
3427     const unsigned int keyAPPLEFT    = VK_LWIN;
3428     const unsigned int keyALT        = VK_LMENU;
3429     const unsigned int keySPACE      = VK_SPACE;
3430     const unsigned int keyALTGR      = VK_CONTROL;
3431     const unsigned int keyAPPRIGHT   = VK_RWIN;
3432     const unsigned int keyMENU       = VK_APPS;
3433     const unsigned int keyCTRLRIGHT  = VK_CONTROL;
3434     const unsigned int keyARROWLEFT  = VK_LEFT;
3435     const unsigned int keyARROWDOWN  = VK_DOWN;
3436     const unsigned int keyARROWRIGHT = VK_RIGHT;
3437     const unsigned int keyPAD0       = 0x60;
3438     const unsigned int keyPAD1       = 0x61;
3439     const unsigned int keyPAD2       = 0x62;
3440     const unsigned int keyPAD3       = 0x63;
3441     const unsigned int keyPAD4       = 0x64;
3442     const unsigned int keyPAD5       = 0x65;
3443     const unsigned int keyPAD6       = 0x66;
3444     const unsigned int keyPAD7       = 0x67;
3445     const unsigned int keyPAD8       = 0x68;
3446     const unsigned int keyPAD9       = 0x69;
3447     const unsigned int keyPADADD     = VK_ADD;
3448     const unsigned int keyPADSUB     = VK_SUBTRACT;
3449     const unsigned int keyPADMUL     = VK_MULTIPLY;
3450     const unsigned int keyPADDIV     = VK_DIVIDE;
3451 
3452 #else
3453     // Define random keycodes when no display is available.
3454     // (should rarely be used then!).
3455     const unsigned int keyESC        = 1U;   //!< Keycode for the \c ESC key (architecture-dependent)
3456     const unsigned int keyF1         = 2U;   //!< Keycode for the \c F1 key (architecture-dependent)
3457     const unsigned int keyF2         = 3U;   //!< Keycode for the \c F2 key (architecture-dependent)
3458     const unsigned int keyF3         = 4U;   //!< Keycode for the \c F3 key (architecture-dependent)
3459     const unsigned int keyF4         = 5U;   //!< Keycode for the \c F4 key (architecture-dependent)
3460     const unsigned int keyF5         = 6U;   //!< Keycode for the \c F5 key (architecture-dependent)
3461     const unsigned int keyF6         = 7U;   //!< Keycode for the \c F6 key (architecture-dependent)
3462     const unsigned int keyF7         = 8U;   //!< Keycode for the \c F7 key (architecture-dependent)
3463     const unsigned int keyF8         = 9U;   //!< Keycode for the \c F8 key (architecture-dependent)
3464     const unsigned int keyF9         = 10U;  //!< Keycode for the \c F9 key (architecture-dependent)
3465     const unsigned int keyF10        = 11U;  //!< Keycode for the \c F10 key (architecture-dependent)
3466     const unsigned int keyF11        = 12U;  //!< Keycode for the \c F11 key (architecture-dependent)
3467     const unsigned int keyF12        = 13U;  //!< Keycode for the \c F12 key (architecture-dependent)
3468     const unsigned int keyPAUSE      = 14U;  //!< Keycode for the \c PAUSE key (architecture-dependent)
3469     const unsigned int key1          = 15U;  //!< Keycode for the \c 1 key (architecture-dependent)
3470     const unsigned int key2          = 16U;  //!< Keycode for the \c 2 key (architecture-dependent)
3471     const unsigned int key3          = 17U;  //!< Keycode for the \c 3 key (architecture-dependent)
3472     const unsigned int key4          = 18U;  //!< Keycode for the \c 4 key (architecture-dependent)
3473     const unsigned int key5          = 19U;  //!< Keycode for the \c 5 key (architecture-dependent)
3474     const unsigned int key6          = 20U;  //!< Keycode for the \c 6 key (architecture-dependent)
3475     const unsigned int key7          = 21U;  //!< Keycode for the \c 7 key (architecture-dependent)
3476     const unsigned int key8          = 22U;  //!< Keycode for the \c 8 key (architecture-dependent)
3477     const unsigned int key9          = 23U;  //!< Keycode for the \c 9 key (architecture-dependent)
3478     const unsigned int key0          = 24U;  //!< Keycode for the \c 0 key (architecture-dependent)
3479     const unsigned int keyBACKSPACE  = 25U;  //!< Keycode for the \c BACKSPACE key (architecture-dependent)
3480     const unsigned int keyINSERT     = 26U;  //!< Keycode for the \c INSERT key (architecture-dependent)
3481     const unsigned int keyHOME       = 27U;  //!< Keycode for the \c HOME key (architecture-dependent)
3482     const unsigned int keyPAGEUP     = 28U;  //!< Keycode for the \c PAGEUP key (architecture-dependent)
3483     const unsigned int keyTAB        = 29U;  //!< Keycode for the \c TAB key (architecture-dependent)
3484     const unsigned int keyQ          = 30U;  //!< Keycode for the \c Q key (architecture-dependent)
3485     const unsigned int keyW          = 31U;  //!< Keycode for the \c W key (architecture-dependent)
3486     const unsigned int keyE          = 32U;  //!< Keycode for the \c E key (architecture-dependent)
3487     const unsigned int keyR          = 33U;  //!< Keycode for the \c R key (architecture-dependent)
3488     const unsigned int keyT          = 34U;  //!< Keycode for the \c T key (architecture-dependent)
3489     const unsigned int keyY          = 35U;  //!< Keycode for the \c Y key (architecture-dependent)
3490     const unsigned int keyU          = 36U;  //!< Keycode for the \c U key (architecture-dependent)
3491     const unsigned int keyI          = 37U;  //!< Keycode for the \c I key (architecture-dependent)
3492     const unsigned int keyO          = 38U;  //!< Keycode for the \c O key (architecture-dependent)
3493     const unsigned int keyP          = 39U;  //!< Keycode for the \c P key (architecture-dependent)
3494     const unsigned int keyDELETE     = 40U;  //!< Keycode for the \c DELETE key (architecture-dependent)
3495     const unsigned int keyEND        = 41U;  //!< Keycode for the \c END key (architecture-dependent)
3496     const unsigned int keyPAGEDOWN   = 42U;  //!< Keycode for the \c PAGEDOWN key (architecture-dependent)
3497     const unsigned int keyCAPSLOCK   = 43U;  //!< Keycode for the \c CAPSLOCK key (architecture-dependent)
3498     const unsigned int keyA          = 44U;  //!< Keycode for the \c A key (architecture-dependent)
3499     const unsigned int keyS          = 45U;  //!< Keycode for the \c S key (architecture-dependent)
3500     const unsigned int keyD          = 46U;  //!< Keycode for the \c D key (architecture-dependent)
3501     const unsigned int keyF          = 47U;  //!< Keycode for the \c F key (architecture-dependent)
3502     const unsigned int keyG          = 48U;  //!< Keycode for the \c G key (architecture-dependent)
3503     const unsigned int keyH          = 49U;  //!< Keycode for the \c H key (architecture-dependent)
3504     const unsigned int keyJ          = 50U;  //!< Keycode for the \c J key (architecture-dependent)
3505     const unsigned int keyK          = 51U;  //!< Keycode for the \c K key (architecture-dependent)
3506     const unsigned int keyL          = 52U;  //!< Keycode for the \c L key (architecture-dependent)
3507     const unsigned int keyENTER      = 53U;  //!< Keycode for the \c ENTER key (architecture-dependent)
3508     const unsigned int keySHIFTLEFT  = 54U;  //!< Keycode for the \c SHIFTLEFT key (architecture-dependent)
3509     const unsigned int keyZ          = 55U;  //!< Keycode for the \c Z key (architecture-dependent)
3510     const unsigned int keyX          = 56U;  //!< Keycode for the \c X key (architecture-dependent)
3511     const unsigned int keyC          = 57U;  //!< Keycode for the \c C key (architecture-dependent)
3512     const unsigned int keyV          = 58U;  //!< Keycode for the \c V key (architecture-dependent)
3513     const unsigned int keyB          = 59U;  //!< Keycode for the \c B key (architecture-dependent)
3514     const unsigned int keyN          = 60U;  //!< Keycode for the \c N key (architecture-dependent)
3515     const unsigned int keyM          = 61U;  //!< Keycode for the \c M key (architecture-dependent)
3516     const unsigned int keySHIFTRIGHT = 62U;  //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent)
3517     const unsigned int keyARROWUP    = 63U;  //!< Keycode for the \c ARROWUP key (architecture-dependent)
3518     const unsigned int keyCTRLLEFT   = 64U;  //!< Keycode for the \c CTRLLEFT key (architecture-dependent)
3519     const unsigned int keyAPPLEFT    = 65U;  //!< Keycode for the \c APPLEFT key (architecture-dependent)
3520     const unsigned int keyALT        = 66U;  //!< Keycode for the \c ALT key (architecture-dependent)
3521     const unsigned int keySPACE      = 67U;  //!< Keycode for the \c SPACE key (architecture-dependent)
3522     const unsigned int keyALTGR      = 68U;  //!< Keycode for the \c ALTGR key (architecture-dependent)
3523     const unsigned int keyAPPRIGHT   = 69U;  //!< Keycode for the \c APPRIGHT key (architecture-dependent)
3524     const unsigned int keyMENU       = 70U;  //!< Keycode for the \c MENU key (architecture-dependent)
3525     const unsigned int keyCTRLRIGHT  = 71U;  //!< Keycode for the \c CTRLRIGHT key (architecture-dependent)
3526     const unsigned int keyARROWLEFT  = 72U;  //!< Keycode for the \c ARROWLEFT key (architecture-dependent)
3527     const unsigned int keyARROWDOWN  = 73U;  //!< Keycode for the \c ARROWDOWN key (architecture-dependent)
3528     const unsigned int keyARROWRIGHT = 74U;  //!< Keycode for the \c ARROWRIGHT key (architecture-dependent)
3529     const unsigned int keyPAD0       = 75U;  //!< Keycode for the \c PAD0 key (architecture-dependent)
3530     const unsigned int keyPAD1       = 76U;  //!< Keycode for the \c PAD1 key (architecture-dependent)
3531     const unsigned int keyPAD2       = 77U;  //!< Keycode for the \c PAD2 key (architecture-dependent)
3532     const unsigned int keyPAD3       = 78U;  //!< Keycode for the \c PAD3 key (architecture-dependent)
3533     const unsigned int keyPAD4       = 79U;  //!< Keycode for the \c PAD4 key (architecture-dependent)
3534     const unsigned int keyPAD5       = 80U;  //!< Keycode for the \c PAD5 key (architecture-dependent)
3535     const unsigned int keyPAD6       = 81U;  //!< Keycode for the \c PAD6 key (architecture-dependent)
3536     const unsigned int keyPAD7       = 82U;  //!< Keycode for the \c PAD7 key (architecture-dependent)
3537     const unsigned int keyPAD8       = 83U;  //!< Keycode for the \c PAD8 key (architecture-dependent)
3538     const unsigned int keyPAD9       = 84U;  //!< Keycode for the \c PAD9 key (architecture-dependent)
3539     const unsigned int keyPADADD     = 85U;  //!< Keycode for the \c PADADD key (architecture-dependent)
3540     const unsigned int keyPADSUB     = 86U;  //!< Keycode for the \c PADSUB key (architecture-dependent)
3541     const unsigned int keyPADMUL     = 87U;  //!< Keycode for the \c PADMUL key (architecture-dependent)
3542     const unsigned int keyPADDIV     = 88U;  //!< Keycode for the \c PADDDIV key (architecture-dependent)
3543 #endif
3544 
3545     const double PI = 3.14159265358979323846;   //!< Value of the mathematical constant PI
3546 
3547     // Define a 10x13 binary font (small sans).
3548     static const char *const data_font_small[] = {
3549       "                      UwlwnwoyuwHwlwmwcwlwnw[xuwowlwmwoyuwRwlwnxcw     Mw                    (wnwnwuwpwuypwuwoy"
3550       "ZwnwmwuwowuwmwnwnwuwowuwfwuxnwnwmwuwpwuypwuwZwnwnwtwpwtwow'y    Hw   cwnw  >{ jw %xdxZwdw_wexfwYwkw 7yowoyFx=w "
3551       "ry    qw %wuw  !xnwkwnwoyuwfwuw[wkwnwcwowrwpwdwuwoxuwpwkwnwoyuwRwkwnwbwpwNyoyoyoyoy;wdwnxpxtxowG|!ydwnwuwowtwow"
3552       "pxswqxlwnxnxmwDwoyoxnyoymwp{oyq{pyoy>ypwqwpwp{oyqzo{q{pzrwrwowlwqwswpwnwqwsxswpypzoyqzozq}swrwrwqwtwswswtxsxswq"
3553       "ws}qwnwkwnydwew_wfwdwkwmwowkw(w0wmwmwGwtwdxQw swuwnwo{q{pynwp|rwtwtwqydwcwcwcwmwmxgwqwpwnzpwuwpzoyRzoyoyexnynwd"
3554       "z\\xnxgxrwsxrwsyswowmwmwmwmwmwmwo}ryp{q{q{q{nwmwnwmwozqxswpyoyoyoyoyeyuwswrwrwrwrwrwrwrwrwqwrwmwtwnwmwnwuwpwuyp"
3555       "wuwoyZwmwnwuwowuwmwqwkwuwowuwoxnwuxowmwnwuwpwuypwuwZwmwnwuwowuwnwowmwtw\\wuwuwqwswqwswqwswqwswEwqwtweypzr~qyIw "
3556       "rwswewnwuwowuwozswtwuwqwtwmwnwlwowuwuwowOxpxuxqwuwowswqwswoxpwlwjwqwswqwsw<wrwowrwuwqwrwqwswrwswpwmwmwrwswrwowl"
3557       "wqwtwownxsxsxswqwswqwswqwswrwswqwrwowpwrwrwqwtwswswswswqwswmwpwmwlwoxuxSw_wfwdwYwkw(w0wmwmwGwtwoxnwNw uwswpwuwp"
3558       "wmwmwswq{rwrwrwtwtwrwswfydwdyZwnwtwrwqwrwswowowdwrwqxuwSwrwfwuwnwlwnw[yuw[wowtwgwswqwswqwswewuwowuwowuwowuwowuw"
3559       "nwowuwowswqwmwmwmwjwmwnwmwowswrxswqwswqwswqwswqwswqwswrwrwqwswrwrwrwrwrwrwrwrwqwswqzpwtw #w DwPwtwtwswqwswuwuwu"
3560       "wswswuwswqwGwqxtwf{qzr~r{qzqwrwpxowtwrw rzcwnwuwq}rwuwqwtwuwqwtwmwnwlwnynwOwowswowkwmwpwuwpwmwjwpwswqwswowmwjwi"
3561       "wjxswsytwrwuwqwrwrwmwrwqwmwnwmwrwowlwqwuwnwnxsxswuwtwrwqwrwswrwqwswswqwjwpwrwqwswrwtwtwqwuwowuwmwowmwlwpxsx]ypz"
3562       "oyozpypzozqznwmwowtwnwqzuyrzoypzozqwuxoypzpwswrwrwrwtwtwswrwrwrwq{owmwmwQyuwqwtwmwoxnypzqxswowowswqwswqwtxr|rwt"
3563       "wtwqyp{q{qwswpwuwownwnwqwsxuwuxswrwrwtwtwswqwrwmwuwuwnwnwowtwpwuwuwewnzpwn{pwuwnwnxgwtxtwrwtwowtw_wuytwgynwmwlw"
3564       "gwswpyuw[wowtwqwtwpwtwpwtwowuwmwnwuwowuwowuwowuwowuwowuwqxuwpwlwmwmwmwjwmwnwmwowrwswuwtwrwqwswqwswqwswqwswqwrwt"
3565       "wqwswuwswrwrwrwrwrwrwrwpwuwpwswqwuwnyoyoyoyoyoyqyuyqyoyoyoyoymwqwjwmwnypzoyoyoyoyoynwnzqwswqwswqwswqwswrwrwqzqw"
3566       "rw^}s}swtwtwswtwtwswtwtwK}rwuwe{s~t~s}rwtwqwrwpxowtwrw qwawewtwpwuwpxuwpycwlwnynwOwowswowkwpypwtwpzpzmwoypwsw[y"
3567       "r}rymwrwtwtwtwrwuwq{qwmwrwq{q{rwm|owlwqxmwnwuwuwuwswuwtwrwqwrwswrwqwswswqylwpwrwqwswrwuwuwuwpwmwmwnwmwlwMwqwswq"
3568       "wmwswqwswpwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqxnwswpwnwswrwrwrwtwtwrwtwqwrwmwqxlwlx]xuxrwtyqwuwlwpwtwpwmw"
3569       "swqwtxpxowswrwqwswtwuxrwtwqwtwtwrwswrwswnwo{pwuwnxpwnwqwswtwtwswrwrwtwtwswuyuwswjwkwowpwrwowcwowuwnwnwswqxuxowo"
3570       "wtwhwuwrwrzpwtwq}jwuwtwuw_}qyoxfwswpyuwowdyoxowtwryuwqyuwqyuwmwnwuwowuwowuwowuwowuwowuwqwt{twl{q{q{q{nwmwnwmwpz"
3571       "twswuwtwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwowowswqwuwkwmwmwmwmwmwowswswmwswqwswqwswqwswnwqwjwmwowswqws"
3572       "wqwswqwswqwswqwswqwswgwtxqwswqwswqwswqwswrwrwqwswrwrw^wtwtwswqwswuwuwuwswuwswswqwHwowuwf}t~s|r}swrwrwrwqwtwpwtw"
3573       "r~#zcwewtwoynwuxtwtwswgwlwowuwuwr}gyexowswowlwlwrwswlwqwswowowswpz^yayqwqwtwtwuwrwswrwrwrwmwrwqwmwnwsyswrwowlwq"
3574       "wuwnwnwuwuwuwswtwuwrwqwrzqwqwszmyowpwrwpwuwqwuwuwuwpwmwmwnwlwmwPzqwswqwmwswq{pwnwswqwswowmwoxlwqwswswswswqwswqw"
3575       "swqwswqwlxnwnwswqwtwqwuwuwuwqxowtwmwnwmwmwoytwiwtwtwswswpwtxqzpwswpxowswpwuwowuwpwswrwtwtwswtwtwrwtwqwtwtwrwswr"
3576       "wswnwowswqwswowownwqwswtwtwswrwqwuwuwrwuyuwt~pwq~pwq~pwcwowuwozpwswowewswiwuwrwiwtwjwjwuytw\\wRwswoxuwHwtwpwswq"
3577       "wtxqwswqxowswqwswqwswqwswqwswqwswrwtwpwlwmwmwmwjwmwnwmwowrwswtwuwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwow"
3578       "owswqwtwozpzpzpzpzpzr~swm{q{q{q{nwqwjwmwowswqwswqwswqwswqwswqwswqwswr}rwuwuwqwswqwswqwswqwswqwtwpwswqwtw\\wuwuw"
3579       "qwswqwswqwswqwswJ}qxf}t~rzp{rwrwrwrwqwtwpwtwrw qwawg}owuwpwuwtwuwswuwfwlwmwmwPwnwswowmwkwr|mwqwswowowswmw^yo}oy"
3580       "qwqwszq{rwrwrwmwrwqwmwnwqwswrwowlwqwtwownwtwtwswtwuwrwqwrwnwqwswtwkwowpwrwpwuwqwuwuwuwqwuwnwnwmwlwmwQwswqwswqwm"
3581       "wswqwlwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqwjwownwswqwtwqwuwuwuwqxowtwnwmwmwmwpwtyhwtwtwswswpwswqwtwpwswqw"
3582       "mwswpwuwpwtwpwswrwtwtwswtwtwrwtwqwtwtwrwswrwswnwowswqwswpwnwnwqwsxuwuxswrwpyqwqwswjwkwqwuwuwrwrwqwuwuwewowuwnwn"
3583       "wswq{ownxuwiwtxtwrzpwtwkwjwuwtwuw\\wRwswnwuwSzpwtwowtxqwrwrwtxrxn{q{q{q{q{q{s{pwlwmwmwmwjwmwnwmwowrwswtwuwrwqws"
3584       "wqwswqwswqwswqwrwtwqwuwswswrwrwrwrwrwrwrwowozpwswqwswqwswqwswqwswqwswqwswswswowmwmwmwmwjwqwjwmwowswqwswqwswqwsw"
3585       "qwswqwswqwswgwuwuwqwswqwswqwswqwswqwtwpwswqwtw[yoyoyoyoyGwmwdwuwuwpxnxnyqwrwqwtwpwtwoxpw rwswSwuwmwuwpwuwtwuxsw"
3586       "ewlwcwPwnxuxownwnwswnwlwqwswowowswnwZygygwkwswrwrwqwswrwswpwmwmwrwswrwowlwqwswpwnwqwswsxqwswqwmwswrwswqwrwowpxt"
3587       "xowowswqwswowowlwlwmwQwswqwswqwmwswqwswpwnwswqwswowmwowtwnwqwswswswswqwswqwswqwswqwmwswpwnwswpxowswqwtwoxnwlwmw"
3588       "mw[xuxrwtxpwswqwtwpwswqwmwswpypwtwpwswrwtwtwsxuwuxrwtwqwtwtwrwswrwswnwnwuwpwswqwmwmwswq{rwrwowowswqwkwlwoypwtwo"
3589       "ydwowuwnwn{owmwlwgwrwfwtw^wrw6wswnwuwJwtwowtzswrwrwtzswmwswqwswqwswqwswqwswqwswswswowswqwmwmwmwjwmwnwmwowswrwsx"
3590       "qwswqwswqwswqwswqwswrwrwqwswrxtxrxtxrxtxrxtxowowmwswqwswqwswqwswqwswqwswqwswswtxowmwswqwswqwswqwswnwqwjwmwowswq"
3591       "wswqwswqwswqwswqwswqwswowoxtwqwswqwswqwswqwswpxowswpx Wwlwbwnxcwpwrwqzpwtwoxo|!ydwfwtwozpwsxszuxgxnxcwmwcwoxmyp"
3592       "{q{pymwpzoyowmypymwmwjwiwkwowrwrwqws{oyqzo{qwlzrwrwowlwqwrwq{rwqwswsxpypwlyqwrwqznwoznwowswrxsxpwp}qwkwnwPzqzoy"
3593       "ozpyowmzqwswowmwowswowqwswswswswpypzozqwlynxozpxowswrwrwpwn{owmwmwQxuxqzoxnyoypwswowpwrwqzpxuxq{qwtxq{qzpylwoyq"
3594       "}r{qwnyuypwpwrwownydwcwcwcwnzq{rwqwpwmwkwgzHz]}U|owuw@wqwswrytwqwqyqwqwswqwswqwswqwswqwswqwuwr{ryp{q{q{q{nwmwnw"
3595       "mwozqwsxpyoyoyoyoygwuypzpzpzpznwowmwuypzpzpzpzpzpzryuzryoyoyoyoymwqwjwmwnypwswpyoyoyoyoyfzozpzpzpzpwnzow    \\w"
3596       "OwnwXw[w SwGz kx0x lxdx gw[w=wiw*wbyowoyGwKwowewawcwow  YwOwoz Ewjwuwdw 7w   9w  Iwnwlw    \\w      0|*y[x=wiw,"
3597       "xWw=wKwowewawcwow  Yw  hwVx 8w   9w  Jxmwnxp" };
3598 
3599     // Define a 26x32 font (normal sans).
3600     static const char *const data_font_normal[] = {
3601       "                                                      #{}~}a{|y~f{|y~}f{|}|x{}|j{|y}y{|y}g{}y~}|2y~|a{}~}f{}y~|"
3602       "gy}|yy}|i{}~}a{}~}f{}y~}gy}|yx}N{|}|x{}|hy~|ay~|fx~|g{}y~|y{}~j{|y~|yy~}5{}~}a{}~}f{}y~}gy~}yy~}e{|y~          "
3603       "                                                      2{}~}c{|y~f{|y~}~}h{}w~y}~|j{}y~y{}y~h{}~y}y~|2y~|c{}~}f{"
3604       "}~y}~|hy~}yy~}hy~|c{|~}f{|~y}~|hy~}y{}y~O{}w~y}~|gy~|cy~|fy~|}~|i{|~y}y~}~}j{|y~|yy~}4{}~|c{}~}f{}~|}~}hy~}yy~}"
3605       "ey~|  g{|}y~}                                                              J{}~|dy~|fy~y{}~|i{~}{|}y~}i{}y~y{}y"
3606       "~i{|~}x{~}2{|y~d{|~}f{|~}yy~hy~}yy~}gy~cy~f{|~}y{}~|iy~}y{}y~P{|~}{|}y~|ey~d{}~|fy~x{}~|j{}~y{|}~}i{|y}|yy}|3{}"
3607       "~|e{}~}f{}~|y{|~|iy}|yx}f{}~|  fy~y}~}           k{|y~|                       /{|y~|                        y{}"
3608       "~}   Xy|e{|}|f{|}wy|5{|~|x{}~1{|}|ey|ey|wy|M{|}|e{|}|fy|wy|    g{|}|3{|y~|_{}~}g{|y~2{}~|y{}~|5{|y~^y~}g{}y~N{|"
3609       "}|^{|}|g{|}| s{}~}_{|y~|gy~} Z{}~}_{|y~|gy~}    )y}|                       -{|y~                    Jy}|yy}|   "
3610       "X{}y~    4{|~}y{|~}     P{|  n{|y~`{|y~fx~}3{}~x{|~|4{}~}`{}~}g{|x~}N{}~}`{|y~|gx~| sy~|`y~|g{}x~| Z{}~}`y~}g{}"
3611       "x~|I{}y~  1{|x~|oi| r{|~|O{|d{|y}|j{|y}|u{|y}|h{|   \"{|}x~}|Ny~}g{|y~y{|~}g{|~}x{|~}i{|~l{|}y~}|s{|~}l{|}x~}|e"
3612       "{|y~by~g{}~}b{~} S{|y~i{|}x~}|i{|y}x~|i{|y}x~y}i{|y}w~}|d{}x~kq~|i{|}w~}|m{}o~k{|}x~y}h{|}x~}| B{|}w~}L{|x~j{|s"
3613       "~y}g{|}w~}|o{|s~y}|k{|o~n{|p~j{|}w~y}|o{|y~|ry~}k{|y~|e{|y~|j{|y~|t{|x~n{|y~|i{|x~}r{|x~}s{|x~|sy~}l{|}x~y}|l{|"
3614       "s~}|i{|}x~y}|m{|s~}|hy}w~y}|ok~}r{}y~r{|y~|r{}y~|p{}y~vy~}t{|x~sy~}ux~rx~q{}y~|r{}y~|r{|l~l{}v~hy~|c{|v~|f{|}|L"
3615       "{}~}M{}y~@{}~}O{|}w~R{}y~`{|y~d{|y~h{}y~`{|y~    ay}y~}h{}~}h{}y~y} Wy}x~}|O{|y}w~}| xx~}  I{|}x~}f{|x~i{|o~m{|"
3616       "o~m{|y}x~}|f{}y~k{|m~}r{|y~|w{}y~vy~}n{|}x~y}|My}Iy}|J{}~| q{|}x~y}T{}y~r{}~}R{}w~}|j{|y~}yy~}O{|}w~} \\{|t~}h{"
3617       "|}y~}M{|}x~}|h{|}x~}|e{|y~L{|}t~|7y}y~}f{|}x~}Uy|y}|py}p{|n{|t{|}w~}r{|y~P{|x~e{|x~e{|x~e{|x~e{|x~f{}v~|jk~|o{|"
3618       "}w~}|m{|o~n{|o~n{|o~n{|o~j{|y~|e{|y~|e{|y~|e{|y~|k{|s~y}|m{|x~|sy~}l{|}x~y}|i{|}x~y}|i{|}x~y}|i{|}x~y}|i{|}x~y}"
3619       "|O{|}x~y}|y{|~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~|r{}y~|p{|y~|b{|}x~}|h{}~}b{|y~|g{}~|}~|h{|y~}|"
3620       "{}~iy~}yy~}i{}~|y{}~|3{}~}b{|~}fy~{}~|i{|y~|{|y~}iy~|ay~}g{}~}y~gy~}yy~}i{|y~}wy|j{|y~}y{}~hy~|b{}~}g{|~}|~}h{|"
3621       "}y~}{|~}j{}y~y{}y~|4y~|b{}~}g{|~}{y~h{}y~y{|y~|f{|y~|k{}y~by~}y{}y~  ev~o{}k~} r{}~O{|~e{}v~l{}v~w{}w~}j{}~ Y{}"
3622       "o~  S{|s~}Oy~}g{|y~y{|~}g{}~|x{}~|i{|~m{|y~y}y~|ty~l{}t~}f{|y~c{}~}fy~b{~} S{}~}j{}t~}kt~|j{}r~|l{|r~}f{|w~kq~|"
3623       "j{}s~|n{}p~}m{|r~|l{|s~| D{}s~|i{|y}y~y}|hw~|k{|p~|k{|q~}q{|p~}|m{|o~n{|p~l{|p~}q{|y~|ry~}k{|y~|e{|y~|j{|y~|u{|"
3624       "x~m{|y~|i{|w~|sw~}s{|w~sy~}n{}r~}m{|q~}l{}r~}n{|q~}k{|q~|pk~}r{}y~r{|y~|r{|y~}py~}v{}y~t{}x~|u{|y~|u{|y~}t{}y~|"
3625       "py~}s{|y~}q{|l~l{}w~}h{}~}c{|v~|gw~}L{}~|N{}y~@{}~}P{|u~R{}y~`{|y~d{|y~h{}y~`{|y~  e{}y~  {}w~}h{}~}h{}v~ Ys~}Q"
3626       "{|r~| yv~  K{}t~|hw~|j{|o~m{|o~n{}r~|h{}y~k{|m~}r{|y~|w{}y~vy~}p{}r~}O{}x~Jy~|K{}x~|/{~|f{}t~}Ty~|t{|y~|Ss~j{|y"
3627       "~}yy~}i{|}v~}|j{}~w}y~ v{|}v~}|k{|t~}i{|y~}x~N{}~}|}y~|i{|y}y|}~}fy~|N{|u~y}y~|8{|~y}~}g{|y~x}y~W{|w~}q{}~}s{}x"
3628       "~}q{|y~t{|}x|}~}s{}~|Pw~|fw~|fw~|fw~|fw~|fw~|j{|k~|q{|q~}o{|o~n{|o~n{|o~n{|o~j{|y~|e{|y~|e{|y~|e{|y~|k{|o~|o{|w"
3629       "~sy~}n{}r~}l{}r~}l{}r~}l{}r~}l{}r~}R{}r~}|y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|py~}s{|y~}o{|y~|cs~}h{"
3630       "}~}cy~|g{|~}yy~i{|y~}w~}iy~}yy~}hy~y}~}1y~|d{|y~f{}~|{|~}i{|y~|{|y~}i{|y~b{}~}g{|~}{|~}hy~}yy~}h{|y~y}y~}|k{|y~"
3631       "}y~}~}h{|y~c{|~}fy~y{|~|i{}~}v~|j{}y~y{}y~|4{|y~c{|~}fy~y{|~}i{}y~y{|y~|fy~|j{}y~by~}y{}y~  f{|y~{}~|p{|k~| r{~"
3632       "}Oy~}g{}u~}n{}t~y{}u~}l{}y~} \\{}m~  T{|x~}|{y|y~|Py~}g{|y~y{|~}gy~|xy~|i{|~my~|y{|y~u{}~}m{}y~}|y{|y}f{|y~d{|y"
3633       "~e{}~}hy|x{~}x{| Wy~|k{|y~}|{|}y~}lx~y}y~|jx~}x|}x~|m{|~}v|x~}gv~ky~s|j{}x~w|}~|nr|}y~|mx~}|{y|x~|mx~y|{|}y~| E"
3634       "y~}x|}x~k{}q~|k{|w~}k{|y~u|}x~l{}x~}v|}y~|r{|y~u|y}x~}n{|y~q|n{|y~r|m{}x~}v|}x~|r{|y~|ry~}k{|y~|e{|y~|j{|y~|v{|"
3635       "x~l{|y~|i{|w~}t{|w~}s{|w~}ty~}o{}x~}|{y|}x~n{|y~v|}x~|n{}x~}|{y|}x~o{|y~v|}x~}m{|x~}v|}~|pt|y~}u|q{}y~r{|y~|qx~"
3636       "q{|y~|v{|y~|u{}x~|u{}y~|t{}y~|v{|y~}o{|y~|tx~op|}y~}l{}~}e{|y~`{|y~|h{}v~}L{}~|O{}y~@{}~}Py~}|O{}y~`{|y~d{|y~h{"
3637       "}y~`{|y~  e{}y~ !{|y~}e{}~}e{|y~| [{}y~|x{}y~|jy}~y}|ix~|w{|}| w{}y~|  M{}y~|y{|}y~i{|w~}ix~r|m{|y~q|p{|w~}x|}x"
3638       "~}l{|y}x~y}|n{|y~q|y~}r{|y~|w{}y~vy~}q{}x~}|{y|}x~Q{}v~Ky~|L{}v~|0{~|g{|y~}|y{|y}T{}y~t{}~}i{}~}h{}y~|x{|}P{}~y"
3639       "}x|y}~}k{|v{}~| x{}~y}x|y}~}Qy~x{|~}J{|y~cy~g{}~|Mt~y{}~|5{|~}gy~|x{}~}U{|~}r{|y~r{}y|~}qy~|ny~t{|~}P{|w~}g{|w~"
3640       "}g{|w~}g{|w~}g{|w~}fw~}j{}y~y|y~}r|q{}x~}v|}y~|p{|y~q|n{|y~q|n{|y~q|n{|y~q|j{|y~|e{|y~|e{|y~|e{|y~|k{|y~}u|}x~}"
3641       "p{|w~}ty~}o{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~T{}x~}|{y|v~|r{}y~r{|y~|q{}y~r{|y~|q"
3642       "{}y~r{|y~|q{}y~r{|y~|p{|y~|tx~n{|y~|d{}y~|x{}y~|h{}~|e{}~|f{~}x{|~}j{|}xx}hy}|yy}|h{|}y~}/y~dy~|g{|~}x{|~|j{|y}"
3643       "|yy}|h{|~}d{|~}f{}~x{}~|iy}|yy}|iy|w~|h{}~y{|}~}f{|~}e{|~}f{}~|x{}~|j{}|y{|y}|i{|y}y{|y}2{|~}dy~f{}~|x{}~|j{|y}"
3644       "y{|y}|g{}~|i{}y~by}|y{|y}5{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|f{}~}{y|ny~}q{}y~ r{|~O{}x~|hs~|p{|s~y|s~|n{|w"
3645       "~} ^{}y~}|  Ix~|u{}|Py~}g{|y~y{|~}gy~wy~k{|}v~y}|s{|y~w{}~|w{|y~ly~}_{|y~d{}~}dy~|iy~}y{~}{|y~|hy}| o{|y~jx~v{}"
3646       "y~l{|x{|y~|j{}|u{|x~d{}y~|i{}~|}y~ky~|d{|y~}]{}y~m{|y~|v{|y~}n{}y~|v{}y~ E{}u{}y~|n{|x~}|w{|}y~}|m{}y~}y~k{|y~|"
3647       "u{|y~}n{}y~}s{|~|r{|y~|t{|x~|o{|y~|e{|y~|f{}y~}r{}~|r{|y~|ry~}k{|y~|e{|y~|j{|y~|w{}y~}k{|y~|i{|y~}y~t{}~}y~}s{|"
3648       "v~ty~}p{}y~}t{}y~}o{|y~|v{|x~o{}y~}t{}y~}p{|y~|v{|x~mx~r{|iy~}k{}y~r{|y~|q{|y~|r{}y~u{|y~|uy~}~}u{}y~rx~vx~m{}y"
3649       "~u{}y~|e{|y~}k{}~}dy~|a{|y~|i{}y~|{}y~| y{}y~@{}~}Py~|N{}y~0{}y~`{|y~  e{}y~ !{|y~d{}~}dy~} [y~}v{}~}ju~}jy~| n"
3650       "{}y~  N{|y~|v{}~}j{}y~}y~i{|y~}e{|y~|gx~}t{}y~}o{|}q~|p{|y~|ry~}r{|y~|w{}y~vy~}r{}y~}t{}y~}S{}t~Ly~|M{}t~|1{~|g"
3651       "{}y~Ly~|v{|y~|i{}~}hy~}L{|y~|t{|y~|g{|~} {{|y~|t{|y~|T{|~|wy~f{}~|ay~ey|y~7{}t~y{}~|5{|~}h{|~}vy~U{|~}r{}~|p{|~"
3652       "}r{|~}my~ty~O{}y~}y~g{}y~}y~g{}y~}y~g{}y~}y~g{}y~}y~g{|y~}y~jy~}yy~}i{}y~}s{|~|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{"
3653       "|y~|e{|y~|e{|y~|k{|y~|t{|x~}q{|v~ty~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}V{}y~}t{}x~q{}"
3654       "y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|o{}y~u{}y~|n{|y~|e{|y~|v{}~}   A{|}|ey|e{}|wy|Py~}y|y~}   ?{}~}h{}y~ p"
3655       "{}r~|l{}r~|l{}r~|l{}r~|l{}r~|h{}~}k{}y~qy~}1{|~}dy}P{}v~|is~|p{|r~}s~}nu~|h{|w}|k{|y}sy}|jx}|j{|y}t{|y}o{}y~|  "
3656       "H{|y~|Gy~}g{|y~y{|~}h{|~}x{|~}l{}r~}s{|~}w{}~}w{}~|ly~}_{|y~dy~|d{}~}h{|y~|~y}~}|g{}~| o{}~}k{|y~|uy~}i{|y~|a{}"
3657       "y~|e{|y~}j{|~}{}y~ky~|dy~}]{|y~}m{}y~tx~ny~}u{|y~|,{|X{|X{|y~|o{}y~|q{}y~my~}|y~|l{|y~|ty~}o{|x~p{|r{|y~|s{|x~o"
3658       "{|y~|e{|y~|g{|x~p{|q{|y~|ry~}k{|y~|e{|y~|j{|y~|x{}y~}j{|y~|i{|y~|y~|uy~|y~}s{|y~|y~}uy~}q{|x~r{}y~|p{|y~|u{}y~|"
3659       "q{|x~r{}y~|q{|y~|u{}y~|ny~}_y~}k{}y~r{|y~|py~}s{|y~}ty~}v{|y~|y~uy~}r{|y~}x{}y~|ly~}w{|y~}e{|x~j{}~}d{}~}a{|y~|"
3660       "j{}y~|x{}y~| {{}y~@{}~}Py~|N{}y~0{}y~`{|y~  e{}y~ !{}y~d{}~}d{}~} \\{|y~u{}y~j{}x|}y~|kx~| o{|y~|  O{}~}u{|y~jy"
3661       "~}|y~|i{|y~}f{|y~|h{}y~|rx~|q{|w~y}y~y}x~}q{|y~|ry~}r{|y~|w{}y~vy~}s{|x~r{}y~|U{}y~|y~}x~My~|N{}y~|y~|x~|2{~|gy"
3662       "~}g{|p{}m{}y~v{}~}h{}~}h{}~}L{~}xy|}y|x{}~l{|}u~ {{~}p{}~T{|~|wy~f{}~|b{}~}g{}w~|7{}t~y{}~|5{|~}h{}~|v{}~|V{|~}"
3663       "s{|~}o{|~}ry~n{|}~|u{}~}Oy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|l{}y~|yy~}j{|x~p{|p{|y~|e{|y~|e{|y~|e{|"
3664       "y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|rx~q{|y~|y~}uy~}q{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|q{|}q{|"
3665       "}p{|x~s{}x~|r{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|ny~}w{|y~}m{|s~}|l{|y~u{|y~    8{|w{|y~}  _{} G{}y~ r{|"
3666       "x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|i{}~|jy~|s{|y~|1y~}d{~|Q{|t~is~|p{}i~}os~j{|s~|m{"
3667       "|y~sy~|jw~j{|y~|u{}y~p{|y~|  Gx~Fy~}g{|y~y{|~}h{}~}x{}~}m{|y~}{|~x{|}s{|~}w{}~}x{|~}ky~}_{|y~e{|y~c{|y~f{}x~}|e"
3668       "{}~| oy~|k{}y~t{}y~i{|y~|a{|y~|dy~|jy~|{}y~ky~|e{|y~|]{}y~|m{}y~ty~}o{|y~|ty~}/{|}~}Xy~}|[{|y~|p{}y~|o{}y~o{|y~"
3669       "|{y~}l{|y~|ty~}o{}y~|f{|y~|r{}y~|p{|y~|e{|y~|g{}y~|e{|y~|ry~}k{|y~|e{|y~|j{|y~|y{}y~}i{|y~|i{|y~|}~}v{|y~{y~}s{"
3670       "|y~|}y~uy~}q{}y~|qx~p{|y~|u{}y~|q{}y~|qx~q{|y~|u{}y~|o{|y~|_y~}k{}y~r{|y~|p{}y~s{}y~|t{}y~v{|~}{y~|w{|y~|q{}y~|"
3671       "{|y~}k{|y~|xx~dx~|j{}~}d{|y~a{|y~|k{}y~|v{}y~|9{|y}x~y}j{}y~y{}x~}|h{|}x~y}|j{}x~}|{}~}k{|}x~}|j{|s~|i{}x~}|{}~"
3672       "}n{}y~y{}x~}|h{|y~d{|y~h{}y~u{|y~}j{|y~m{}y~y{}x~}w{|}y~}|p{}y~y{}x~}|i{|}x~}|k{}y~y{}x~}|i{}x~}|{}~}k{}y~y{}x~"
3673       "k{|}w~y}|k{|r~l{}~}t{}~}oy~}s{}y~r{}~}v{}y~}v{}~}r{|y~|u{|y~|oy~}s{}y~n{}p~h{}y~d{}~}d{}~} t{}x~}|y{|~}n{|y~u{}"
3674       "~}e{}y~k{|w~y}|g{|}w~y}l{}y~y{}x~}|n{}~}|s{}y~iy~}i{}~}t{}~}p{|y~|r{}y~n{|y}y{}y~}lm~p{}y~x{|y~x{|y~|k{}w~}|j{|"
3675       "}q~|q{}n~ny~|ty~|l{|y~|{y~}h{|y~}g{|y~|hy~}q{|y~}qx~}y{}y~y{|x~|r{|y~|ry~}r{|y~|w{}y~vy~}s{}y~|qx~V{|y~|{y~y|y~"
3676       "}Ny~|O{|y~|{y~|{y~}Ny~}e{|}w~}|jy~}h{|y~r{}~}my~|x{|y~|h{}~}h{|y~}Ny}x{}u~|yy}n{}y~w}y~ y}y{}v~}|xy}T{~}x{|~}f{"
3677       "}~|c{|~}fx|y}|Q{}~}t{}~}ns~y{}~|5{|~}h{}~|v{}~|V{|~}sy~n{|~}s{}~|p{}x~}u{|y~f{|y~|h{|y~|{y~}i{|y~|{y~}i{|y~|{y~"
3678       "}i{|y~|{y~}i{|y~|{y~}i{|y~|{y~}ly~}xy~}j{}y~|d{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|r{|y~}r{|y~|"
3679       "}y~uy~}q{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~qy~}s{|y~}q{}y~|t{}~}x~r{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}"
3680       "y~r{|y~|n{|y~|xx~l{|q~}m{}y~w{|w~l{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}n{|y}x~y}w{|}x~}|l{|}x~y"
3681       "}|j{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|g{|y~d{|y~d{|y~d{|y~e{|}v~|l{}y~y{}x~}|i{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|h{|}x~"
3682       "}|g{|x~f{|}x~}|{}~|o{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}oy~}s{}y~n{}y~y{}x~}|my~}s{}y~;y~}xy~|y{|y~|py~}s{|y"
3683       "~|py~}s{|y~|py~}s{|y~|py~}s{|y~|j{}~|j{}y~sy~}1y~}d{|~Q{|s~}j{}t~o{|i~}p{}s~}kx~}y|}x~m{|y~sy~|k{|w~|jy~}uy~}py"
3684       "~}  Fy~}Fy~}g{|y~y{|~}m{|k~}q{}y~y{|~n{|~}w{}~|xy~j{}y~|`{|y~e{}~}by~|g{|x~}d{}~| p{|y~jy~}t{}y~i{|y~|a{|y~|e{|"
3685       "y~|k{}~}y{}y~ky~|{|g{}y~\\x~l{|y~|v{|y~|o{|y~|tx~i{}y~d{}y~a{|}w~}Xv~}|^x~p{|y~l{}~}p{}y~y{|y~|m{|y~|ty~}ox~e{|"
3686       "y~|qy~}p{|y~|e{|y~|gx~d{|y~|ry~}k{|y~|e{|y~|j{|y~|{}y~}h{|y~|i{|y~y|y~v{}~}{y~}s{|y~|{y~}vy~}qx~p{}y~|q{|y~|u{|"
3687       "y~|qx~p{}y~|r{|y~|u{}y~|ny~}_y~}k{}y~r{|y~|p{|y~|ty~}s{}y~|w{}~|{}~|w{|y~|py~}{x~i{}y~y{}y~|e{}y~|i{}~}cy~|b{|y"
3688       "~|l{}y~|t{}y~|;{|r~|l{}y~|t~|j{}s~|m{|t~|}~}l{}s~|l{|s~|k{|t~|}~}n{}y~|t~|i{|y~d{|y~h{}y~v{}y~}i{|y~m{}y~|t~y{}"
3689       "u~}q{}y~|t~|l{|s~}l{}y~|t~|l{|t~|}~}k{}y~|v~l{|r~k{|s~}l{}~}t{}~}o{}y~sy~}r{|y~v{}x~vy~}q{}y~|w{|y~}n{}y~sy~}n{"
3690       "|p~h{}y~d{}~}d{}~} v{|t~|{}~|n{|y~u{}~}e{|y~|k{|t~|j{}s~|m{}y~|t~|o{}x~|ty~}ix~i{}~}t{}~}py~}q{|y~|p{|y~}{}v~|n"
3691       "m~p{}y~x{|y~x{|y~|ls~|l{|o~|q{}n~o{|y~|t{}~}l{}y~y{}y~|h{}y~}h{|y~|i{|y~|p{}y~r{}y~|x{}y~x{|x~r{|y~|ry~}r{|y~|w"
3692       "{}y~vy~}sx~p{}y~|p{}b{}|yy~|{|}b{}|hy~|i{|}s{}|ly|yy~|y{}My~}g{|r~k{|y~|gx~|}x~}|}y~|m{}y~xy~}g{}~}g{}x~|Q{|~|y"
3693       "y~}|yx|y{|~|p{|~}w{|y~gy|w{|<{|~|y{}~}x|y~|y{|~|U{}y~y}y~e{}~|d{|y~a{}~Q{}~}t{}~}n{}t~y{}~|5{|~}h{}~|v{}~|m{|v{"
3694       "|k{|~}t{}~|n{|~}t{|~}ox|}y~v{}~|f{|y~|h{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{}y~m{|y~|xy"
3695       "~}jx~c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|qx~r{|y~|{y~}vy~}qx~p{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~"
3696       "|sx~p{}y~|r{|y~}u{|x~px~t{}~}{}y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|m{}y~y{}y~|l{|y~v|}x~}n{}y~x{}y~|"
3697       "k{|r~|l{|r~|l{|r~|l{|r~|l{|r~|l{|r~|q{|r~|{}s~n{}s~|l{}s~|k{}s~|k{}s~|k{}s~|i{|y~d{|y~d{|y~d{|y~g{|r~l{}y~|t~|l"
3698       "{|s~}k{|s~}k{|s~}k{|s~}k{|s~}h{|x~h{|q~|n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}o{}y~sy~}n{}y~|t~|n{}y~sy~}<{}~"
3699       "}wy~|x{|y~q{}~}q{|y~q{}~}{~}w{|~y|y~q{}~}t{|~y|y~q{}~}q{|y~j{}~|j{|y~|u{|y~|<q|}y~w|p{|y}uy}Qq~}k{|u~}o{|i~|q{|"
3700       "q~}m{}y~uy~}n{|y~sy~|k{}w~|j{}y~v{|y~|q{|y~  H{}o~My~}fy|xy|m{|k~}q{}~}y{|~n{|y~wy~|y{}~|ix~_y|ey~|by~}i{|}~}~}"
3701       "y~}f{}~| p{|~}jy~}t{|y~|j{|y~|a{}y~f{|}y~}k{|y~x{}y~kt~}|ky~}{|w~}|e{|y~|kx~|x{|y~}n{|y~|tx~i{}y~d{}y~d{|}v~}|q"
3702       "k|p{|}w~}|a{}y~|p{}~|w{|}y~}|{y|xy~|qy~}xy~}m{|y~|u{|y~|p{|y~}e{|y~|qx~p{|y~|e{|y~|h{|y~}d{|y~|ry~}k{|y~|e{|y~|"
3703       "j{|y~|}y~}g{|y~|i{|y~|{y~|wy~|{y~}s{|y~|{}y~vy~}r{|y~}p{|y~|q{|y~|u{}y~|r{|y~}p{|y~|r{|y~|u{}y~mx~}`y~}k{}y~r{|"
3704       "y~|oy~}u{|y~|s{|y~|wy~|{|~}w{}y~o{|v~|hy~}|y~}e{}y~}h{}~}c{}~}b{|y~|m{}y~|r{|y~|<{|}y|x{|}y~l{}w~|y{|x~|lx~}|yy"
3705       "|}|mx~|y{|}x~}mx~y|y{|x~iy~|h{|x~|y{|}x~}n{}w~|y{|x~i{|y~d{|y~h{}y~w{}y~|h{|y~m{}w~|y{|y~}|~}|{|}y~|r{}w~|y{|x~"
3706       "lx~|y{|}y~}m{}w~|y{|x~|mx~|y{|}x~}k{}w~w|ly~}|xy|}i{}y~g{}~}t{}~}o{|y~|u{|y~|r{|y~|ww~vy~|px~wx~m{|y~|u{|y~|f{|"
3707       "y~}h{}y~d{}~}d{}~}6{|}x~|x{}x~|o{|y~}|{|}y~{y~m{|y~v{|y~|dy~|l{}~}x{|x~|l{}y~}|yy|}|m{}w~|y{|x~n{|}~}u{|y~|j{|x"
3708       "~|j{}~}t{}~}q{|y~|py~}q{|x~y|y~y|x~ny|y~}w|}y~}|p{}y~x{|y~x{|y~|mx~|y{|x~|n{|x~|x{}x~y|pu|y~}v|o{|y~s{}y~ly~}xy"
3709       "~}g{}y~|i{|y~|i{}y~o{}y~|sx~w{}y~w{}y~|s{|y~|ry~}r{|y~|w{}y~w{|y~}t{|y~}p{|y~|qy~}_y~|`{|y~|iy~|j{|y~}u{|y~|iy~"
3710       "|Jy~}h{|x~y|~|{|}k{|y~|fp~|ky~}{|y~f{}~}h{}~y}y~}|Sy}y{}~}qy}p{|~}w{|y~h{|~|x{}~<y}x{}~}x{|~}xy}T{|}y~}d{}~|e{|"
3711       "~}`{}~|R{}~}t{}~}n{}t~y{}~|5{|~}h{|~}vy~l{|~|xy}l{|~}u{|~}m{|~}ty~|k{}~|x{|~}e{|y~|hy~}xy~}jy~}xy~}jy~}xy~}jy~}"
3712       "xy~}jy~}xy~}jy~}xy~|n{}y~wy~}k{|y~}c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~r{|y~|{}y~vy~}r{|"
3713       "y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|q{|y~}w{|x~p{|y~}u{|~}y{|y~|s{}y~r{|y~|q{}y~r{|y~|q{}y"
3714       "~r{|y~|q{}y~r{|y~|ly~}|y~}k{|y~|ux~n{}y~y{|y~|j{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y"
3715       "|x{|}y~q{|}y|x{|}v~|x{|y~}px~}|yy|}|mx~y|y{|x~lx~y|y{|x~lx~y|y{|x~lx~y|y{|x~i{|y~d{|y~d{|y~d{|y~gx~|x{|y~}m{}w~"
3716       "|y{|x~lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}i{|x~hx~|y{|}y~}m{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~"
3717       "}t{}~}o{|y~|u{|y~|n{}w~|y{|x~|o{|y~|u{|y~|={|y~vy~|w{}~|s{|y~o{}~|s{|y~{}y~}y{|y~}{}~|s{|y~t{|y~}{}~|s{|y~o{}~|"
3718       "ky~|iy~}u{}y~;k~}qw~u{~|R{}p~|k{}w~}mi~q{|o~|ny~|u{}y~n{|y~sy~|ky~}y~}j{|y~|w{}y~p{}~}  Hx}y~t}|My~}M{|y~x{|~}l"
3719       "{}y~y{|~m{}~}y{}~}y{|~}i{|w~I{|y~|b{}y~j{}~}|{~}{|y~|h{}~| p{}~|jy~}t{|y~|j{|y~|b{|y~}j{}u~}jy~|x{}y~kr~}ly~y}t"
3720       "~}f{}y~i{}t~|ly~}u{|x~|j{}y~d{}y~f{}v~}|nk~}n{|}w~}|e{}y~|p{|~}w{|u~y}~x{|~}r{|y~|x{}y~m{|y~|xy|}y~}o{|y~|e{|y~"
3721       "|q{}y~p{|y~r|m{|y~s|o{|y~|d{|y~q|y~}k{|y~|e{|y~|j{|v~}f{|y~|i{|y~|{}~}x{|~}yy~}s{|y~|yy~}wy~}r{|y~|p{|y~}q{|y~|"
3722       "ux~q{|y~|p{|y~}r{|y~|v{|y~}m{|v~}y|ey~}k{}y~r{|y~|o{}y~u{}y~qy~}x{|y~y{|y~wy~}n{}x~}g{|v~e{|y~}g{}~}c{|y~b{|y~|"
3723       " o{}~}m{}x~ux~m{}y~|f{}y~u{}y~}n{}y~|uy~}jy~|h{}y~u{}y~}n{}x~v{|y~|j{|y~d{|y~h{}y~x{}y~|g{|y~m{}x~v{}x~|vy~}r{}"
3724       "x~v{|y~|n{}y~|v{}y~|n{}x~ux~n{}y~u{}y~}k{}x~h{|y~a{}y~g{}~}t{}~}ny~}u{}y~py~|x{|y~}~|x{|y~o{|y~}y{}y~|l{}~}u{}~"
3725       "}ex~g{}y~d{}~}d{}~}6y~y}y~|{}y~}y~}p{}y~vy~}y~m{|y~x{|}x~c{}~}m{|y~u{}y~l{}~}e{}x~v{|y~|n{|y~u{}~}i{}x~}j{}~}t{"
3726       "}~}q{}y~o{}y~q{}y~|{|y~y{|y~}my~|w{|y~|o{}y~x{|y~x{|y~|n{}y~ux~n{}y~u{}y~|iy~|j{|n~m{|y~|x{}y~f{}y~|j{|y~|i{}y~"
3727       "o{|y~|t{|y~}w{}y~w{|y~}s{|y~|ry~}qy~}w{}y~w{|y~|t{|y~|{r~y|y~}rx~|_y~|_{}y~|jy~|k{|x~s{}y~|jy~|Jx|h{}y~|y{~|h{|"
3728       "y~|f{|y~}|y{}y~|n{|u~{u~|j{}~}i{|~}y{|}y~}T{~|yy~p{|~p{|~}wx~i{|y~|y{}y~ok|X{~|x{}~}x{|~}x{|~?k~}m{}~}_y~|R{}~}"
3729       "t{}~}mt~y{}~|ix|J{|~}gy~|x{}~}l{|y~|y{}~}m{|~}uy~|u{|t{|~}u{}~}j{}~|xy~cy|h{|y~|x{}y~k{|y~|x{}y~k{|y~|x{}y~k{|y"
3730       "~|x{}y~k{|y~|x{}y~k{|y~|x{}y~ny~}wy~}r|t{|y~|c{|y~r|m{|y~r|m{|y~r|m{|y~r|i{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~|s{"
3731       "|y~|yy~}wy~}r{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}p{|y~}y{|x~o{|y~|v{|y~wy~}s{}y~r{|y~|q{"
3732       "}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|l{|v~j{|y~|u{}y~|o{}y~y{|y~`{}~}d{}~}d{}~}d{}~}d{}~}d{}~}i{}x~u{|y~|r{}y~|f{}y~|"
3733       "uy~}n{}y~|uy~}n{}y~|uy~}n{}y~|uy~}j{|y~d{|y~d{|y~d{|y~h{}y~|v{|y~|n{}x~v{|y~|n{}y~|v{}y~|n{}y~|v{}y~|n{}y~|v{}y"
3734       "~|n{}y~|v{}y~|n{}y~|v{}y~|ix|i{}y~|w{|x~|n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}u{}~}m{}x~ux~n{}~}u{}~}<{"
3735       "|~}vy~|w{|~}s{|~}o{|~}s{|~}y{}y~}|y~}y{|~}s{|~}u{|y~}y{|~}s{|~}o{|~}ky~|i{}y~uy~};k~}q{|{y~|w{}~R{|n~o{}o~}|r{|"
3736       "k~}qm~|oy~|u{|y~n{|y~sy~|l{|y~|}y~iy~}wy~}p{}~}  F{|y~|Fy~}M{}~}x{}~}l{|y~}y|~lu~xy~|xx}|q{|y~|x~ty|S{|y~a{}y~j"
3737       "{}|x{~}x{}|h{}~| py~j{|y~|t{|y~|j{|y~|bx~i{}v~}|k{}~}w{}y~k{}y|x{|x~}mw~}|y{|x~|gy~}h{}u~}l{}y~|v{}x~|jx|dx|i{|"
3738       "}w~}|kk~}l{|}v~}|i{}y~|o{}~|wy~}x{}x~wy~rx~w{|y~|n{|q~|n{|y~|e{|y~|q{}y~|q{|p~}n{|q~o{|y~|tu|q{|m~}k{|y~|e{|y~|"
3739       "j{|v~e{|y~|i{|y~|yy~xy~|yy~}s{|y~|y{}y~|xy~}r{|y~|oy~}q{|y~|w{|x~}q{|y~|oy~}r{|y~v|}y~}k{|}t~}gy~}k{}y~r{|y~|o{"
3740       "|y~}vy~}q{}y~x{|~}xy~|y{|y~|n{|x~e{}x~|ex~f{}~}by~|c{|y~| o{|y~m{}y~|u{|y~|ny~}ey~}u{|y~}ny~|t{}y~jy~|hy~}u{|y~"
3741       "}n{}y~|u{}~}j{|y~d{|y~h{}y~y{}y~|f{|y~m{}y~|v{|x~u{}y~r{}y~|u{}~}ny~}ty~}n{}y~|u{|y~|oy~}u{|y~}k{}y~|h{|y~a{}y~"
3742       "g{}~}t{}~}n{}y~uy~}p{}~}x{}~}|~}x{}~}n{|y~y|y~}k{|y~uy~|f{}y~f{}~}d{}~}d{}y~7{}~x{|y~|~}x{}~py~}v{}x~}m{|y~{|v~"
3743       "|i{}y~}|{}~}my~|ty~}m{}~}e{}y~|u{}~}my~|vy~|j{|y~}y~j{}~}t{}~}qx~o{|y~|ry~}y{|y~x{}y~my~|w{|y~|o{}y~x{|y~x{|y~|"
3744       "ny~|u{|y~|oy~}ty~}iy~|j{|n~mx~w{|y~|g{|y~}j{|y~|i{}y~o{|y~|t{|y~|w{}y~vy~}s{|y~|ry~}qx~w{}y~w{}y~|t{|y~|{r~|{y~"
3745       "}sx~|^y~|^{}y~|ky~|l{|x~q{}y~|ky~|5{|y~|x{~|h{|y~|f{}~}v{|~}mw}v~w}|j{}~}i{}~|w{|y~}U{~y{|y~p{|~ox~y}~}y~j{}y~|"
3746       "y{}y~nk~}Y{~w{}~}y{|}~|x{|~?k~}n{}y~v|ix}|}y~}Q{}~}t{}~}m{|u~y{}~|iy~}Ly|}~}y|i{|y~x}y~j{}y~|y{}y~n{|~}v{|~}v{|"
3747       "y~}u{|~}v{|y~y{}w~}wx|{|}y~x{}~|uy~}Wx~w{|y~|lx~w{|y~|lx~w{|y~|lx~w{|y~|lx~w{|y~|l{}y~w{|y~|p{}y~|wo~t{|y~|c{|p"
3748       "~}n{|p~}n{|p~}n{|p~}j{|y~|e{|y~|e{|y~|e{|y~|mq~u{}y~|s{|y~|y{}y~|xy~}r{|y~|oy~}t{|y~|oy~}t{|y~|oy~}t{|y~|oy~}t{"
3749       "|y~|oy~}o{|y~}|x~n{|y~|vy~|wy~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|k{}x~|j{|y~|u{|y~|o{}y~y{|y~|a{|y~d{"
3750       "|y~d{|y~d{|y~d{|y~d{|y~i{|y~|t{}~}ry~}ey~|t{}y~ny~|t{}y~ny~|t{}y~ny~|t{}y~j{|y~d{|y~d{|y~d{|y~hy~}ty~}n{}y~|u{}"
3751       "~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}Ty~}w{|~}y~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{|y~uy~|m{}y~|u{"
3752       "|y~|o{|y~uy~|<{}~u|y~}w|{y~s{}~n|{y~s{}~|x{}w~}wy~s{}~|v{|y~}wy~s{}~|vy~}vy~ky~|i{|y~|w{|y~|4{|y~}i{}~}w{~}Rm~}"
3753       "qk~|r{}l~q{|m~|p{|y~sy~|o{|y~sy~|l{}y~{|y~|j{}y~x{|y~|p{}i~  V{|y~|F{}~}M{}~|x{}~|k{}v~}|m{|y}|x{}~}y{|v~}s{|y~"
3754       "|{|x~v{|y~S{}y~a{|y~e{~}jt|y~}u| w{|~}j{|y~|t{|y~|j{|y~|cx~|hw|}y~|m{|y~v{}y~cx~|nx~}ux~h{|y~|j{|y~}|{y|x~m{|x~"
3755       "|y{|}w~|={}w~}|E{|}v~|l{|y~|ny~w{}~}v{}y~wy~s{|y~|vy~}n{|q~}|o{|y~|e{|y~|q{}y~p{|p~}n{|q~o{|y~|u{|u~}r{|m~}k{|y"
3756       "~|e{|y~|j{|y~}x~f{|y~|i{|y~|y{}~|{|y~xy~}s{|y~|xy~}xy~}r{|y~|oy~}q{|q~}p{|y~|oy~}r{|r~}h{|y}u~|iy~}k{}y~r{|y~|n"
3757       "x~w{|y~|q{}y~x{}~|x{}~|y{|y~|nw~|ey~}e{}y~|f{}~}b{}~}c{|y~| v{|y}t~m{}y~sy~|o{|y~|f{|y~|ty~}o{|y~|t{|y~jy~|i{|y"
3758       "~|ty~}n{}y~t{}~}j{|y~d{|y~h{}y~{x~|e{|y~m{}y~ty~}u{|y~r{}y~t{}~}o{|y~|t{}y~n{}y~sy~|p{|y~|ty~}k{}y~g{|y~}b{}y~g"
3759       "{}~}t{}~}n{|y~|w{|y~o{|y~x{}~y|y~xy~}m{}w~}iy~|w{}y~f{}y~|g{|y~|d{}~}d{|y~}jy}y~}|t{|}X{~}w{|y~}v{~|r{|y~|v{|x~"
3760       "|m{|y~{|v~}|ku~|y~}n{|y~|t{}y~m{|y~}f{}y~t{}~}m{}y~w{}y~i{}y~{y~}k{}~}t{}~}qy~}ny~}s{|y~|y{|y~x{|y~my~|w{|y~|o{"
3761       "}y~x{|y~x{|y~|o{|y~sy~|p{|y~|t{}y~iy~|j{|y~s|}y~n{|y~}vy~}gx~|j{|y~|i{}y~o{|y~|t{|y~|w{}y~vy~}s{|y~|ry~}q{}y~|x"
3762       "{}y~x{|y~}s{|y~|{r|yy~}tx~}l|ly~|mk|x~|ly~|m{|x~o|x~|ly~|5{}y~w{~|j{}r~}ky~|uy~i{|x~}e{}~}i{}~}v{|y~V{|~y{|y~o{"
3763       "~|o{}x~|{y}k{}y~|y{}~}mk~}Z{|~w{}u~|v{~|@t|y~}t|n{}t~i{|}x~y}P{}~}t{}~}k{|}x~y{}~|iy~}Lt~|i{|}x~}h{|y~|y{}y~|rt"
3764       "~|yy~ux~}wt~|y{}~|{|~}y|}y~x{}v~}|y{|y~u{}y~}m{|y}i{|y~|vy~}m{|y~|vy~}m{|y~|vy~}m{|y~|vy~}m{|y~|vy~}ly~|vy~}py~"
3765       "}vo~t{|y~|c{|p~}n{|p~}n{|p~}n{|p~}j{|y~|e{|y~|e{|y~|e{|y~|m{}s~}u{}y~|s{|y~|xy~}xy~}r{|y~|oy~}t{|y~|oy~}t{|y~|o"
3766       "y~}t{|y~|oy~}t{|y~|oy~}n{|v~m{|y~|wy~|vy~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|u{}y~|o{}y~xx~|"
3767       "i{|y}t~k{|y}t~k{|y}t~k{|y}t~k{|y}t~k{|y}t~p{|y}t~s{|y~s{|y~|f{|y~|t{|y~o{|y~|t{|y~o{|y~|t{|y~o{|y~|t{|y~j{|y~d{"
3768       "|y~d{|y~d{|y~i{|y~|t{}~}n{}y~t{}~}o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~pk~}q{|y~|w{~}{}y~n{}~"
3769       "}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}my~|w{}y~l{}y~sy~|ny~|w{}y~;{}~|}p~{y~s{}~|}p~{y~s{}~|w{}x~vy~s{}~|w{|y~}vy"
3770       "~s{}~|vy~}vy~ky~|h{}~}w{}y~3y~}h{|y~x{|~|S{|l~r{}k~}qm~|p{}o~}o{|y~sy~|o{|y~sy~|ly~}yy~}j{|y~|y{|y~o{}i~  X{|y}"
3771       "y~u}K{}~}My~|xy~i{|}u~}i{|y~y{|y~|{|y~|t{}~}x{|x~w{|y~S{}y~a{|y~|f{~}jk~} x{}~}iy~}t{|y~|j{|y~|d{}y~|b{}y~|ny~|"
3772       "v{}y~c{|y~}nx~|u{}y~|ix~j{|y~|v{|y~}m{|t~y}y~|=x~}?{|x~}l{}y~m{~}wy~|v{|y~w{}~s{}y~u{}y~n{|y~|v{|}y~|p{|y~|e{|y"
3773       "~|q{}y~p{|y~|e{|y~|h{|y~|u{|u~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|}x~g{|y~|i{|y~|y{|y~{}~}xy~}s{|y~|x{|y~|yy~}r{|y~|p{"
3774       "|y~}q{|s~}|o{|y~|p{|y~}r{|q~|ey|w~iy~}k{}y~r{|y~|n{|y~|x{}y~p{|y~|yy~|x{}~}y{}y~n{}v~ey~}f{}y~|e{}~}b{|~}c{|y~|"
3775       " w{}q~m{}y~sy~}o{|y~e{|y~s{}~}o{|n~jy~|i{|y~s{}~}n{}y~t{}~}j{|y~d{|y~h{}v~c{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~s{}"
3776       "y~n{}y~sy~}p{|y~s{}~}k{}y~f{}w~}|f{}y~g{}~}t{}~}m{}~}w{}~}o{|y~|yy~yy~xy~|lw~h{}y~wy~}g{}y~|i{}w~}c{}~}c{|w~}o{"
3777       "|s~}w|}~}X{~|vy~|vy}r{|y~tx~l{|y~w{|}x~|m{}y~x{}x~|n{|y~s{}y~l{|}v~j{}y~t{}~}m{|y~|xy~}j{|y~|{}y~k{}~}t{}~}qy~}"
3778       "vy~|vy~}s{|y~x{|y~x{|y~|ny~|w{|y~|o{}y~x{|y~x{|y~|o{|y~sy~}p{|y~s{}y~iy~|j{|y~s{}y~n{}y~u{}y~h{}y~|i{|y~|i{}y~|"
3779       "p{}y~s{|y~}w{}y~w{|y~}s{|y~|ry~}px~x{}y~x{}y~|s{|y~|p{|y~}u{}h~ly~|m{}h~ly~|mg~ly~|J{}~}i{}y~w{~|j{}r~}ky~ty~|i"
3780       "x~d{}~}i{}y~|vy~|W{|~y{|y~o{~|X{}y~xy~}^{|~}Z{|~w{}~}|}~}u{~|9{}~| v{}~}t{}~}hy~y{}~|iy~} s{|y~}y{}y~|st|y{}~|v"
3781       "{}~|~}wt|y{|~}sy~|wx|v{}~|vy}|~}m{|y~i{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~q{|y~|vy~}k{|y"
3782       "~|c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~|s{|y~|x{|y~|yy~}r{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|"
3783       "y~}t{|y~|p{|y~}t{|y~|p{|y~}m{}x~|m{|y~|x{}~|v{|y~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|ux~n{}y"
3784       "~x{|x~}k{}q~l{}q~l{}q~l{}q~l{}q~l{}q~q{}f~s{|y~e{|n~o{|n~o{|n~o{|n~j{|y~d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{|y"
3785       "~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~pk~}q{|y~wy~y{}y~n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}m{}y~wy~"
3786       "}l{}y~sy~}n{}y~wy~};{}~|}q~}{y~s{}~|}q~}{y~s{}~|x{|w~}wy~s{}~|x{|y~}uy~s{}~|vy~}vy~ky~g{|y~|xy~|4{}y~fy~|yy}R{|"
3787       "l~ri~po~|n{}p~n{|y~sy~|o{|y~sy~|m{|y~|y{}y~iy~}y{}~}o{}~}  H{}r~}K{}~}N{|y~x{|y~f{|~}w~j{}~|y{}~}x{|y~ty~|w{|x~"
3788       "x{}~}S{}y~a{|y~|f{|ik~}T{}u~|Ly~|iy~}t{|y~|j{|y~|e{}y~|`y~}o{}~}u{}y~by~}nx~t{|y~|j{|y~}jy~}t{}y~k{}x~}|{}y~<v~"
3789       "}|hk|g{|}w~}l{}~}n{|~}wy~ty~wy~sy~}t|y~|o{|y~|t{}y~p{|y~}e{|y~|qx~p{|y~|e{|y~|h{|y~}py~}r{|y~|ry~}k{|y~|e{|y~|j"
3790       "{|y~|{}y~}h{|y~|i{|y~|xy~|y~|xy~}s{|y~|wy~}yy~}r{|y~}p{|y~|q{|y~w|k{|y~}p{|y~|r{|y~|w{}x~bx~|jy~}k{}y~r{|y~|my~"
3791       "}xy~}oy~}{|y~w{|y~yy~}o{|y~}{y~}fy~}g{|y~}d{}~}ay~c{|y~| x{}y~}|w{|y~m{}y~sy~}o{|y~e{}y~s{}~}o{|n~jy~|i{}y~s{}~"
3792       "}n{}y~t{}~}j{|y~d{|y~h{}w~}c{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{}y~s{}y~n{}y~sy~}p{}y~s{}~}k{}y~e{|u~}h{}y~g{}~}t{}~}"
3793       "m{|y~wy~|ny~}{|~}y{}~|{|y~k{}y~}h{|y~|y{|y~|h{|y~}h{}w~|c{}~}c{|}x~}oy~}x|}r~|X{~|v{}~|v{~}r{}y~ty~}l{|y~t{}y~n"
3794       "{|y~|wx~|n{|y~s{}y~l{|u~j{}y~t{}~}ly~}y{|y~|j{}y~y{|y~|l{}~}t{}~}r{|y~}vy~|vy~}s{}y~x{|y~x{|y~|ny~|w{|y~|o{}y~x"
3795       "{|y~x{|y~|o{}y~sy~}p{|y~s{}y~iy~|j{|y~|t{}y~ny~}u{|y~|j{|y~}h{|y~|i{|y~}px~ry~}w{}y~w{}y~|s{|y~|ry~}p{|x~|{}y~y"
3796       "{}y~}r{|y~}p{|y~|u{|h~ly~|m{}h~ly~|m{}h~ly~|J{}~}i{}y~w{~|h{|y~|fy~|uy~mv}x~v}|Tx~|wy~U{~|yy~|q{|~or}ly~}xy~|^{"
3797       "|~}Y{~|x{}~}y{}~}w{|~8{}~| v{}~}t{}~}hy~y{}~| yr}h{}~}y{|y~|k{|~}v{|~y|~}ny~r{}~|p{|~}v{|~{|~}m{|y~iy~}t|y~|ny~"
3798       "}t|y~|ny~}t|y~|ny~}t|y~|ny~}t|y~|ny~}t|y~|r{}y~u|y~}k{|y~}c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~"
3799       "|q{}y~r{|y~|wy~}yy~}r{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|n{|w~}m{|y~}y{}~}u{|y~|s{}y~r{|"
3800       "y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|w{|x~}n{}y~v{}x~|n{}y~}|w{|y~m{}y~}|w{|y~m{}y~}|w{|y~m{}y~}|w{|y~"
3801       "m{}y~}|w{|y~m{}y~}|w{|y~r{}y~}|w{|n~s{|y~e{|n~o{|n~o{|n~o{|n~j{|y~d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{}y~s{}y~"
3802       "o{}y~s{}y~o{}y~s{}y~o{}y~s{}y~o{}y~s{}y~pk|p{}y~x{}~|y{}y~n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}m{|y~|y{|y~|l"
3803       "{}y~sy~}n{|y~|y{|y~|;{|~}vy~|w{|~}s{|~}o{|~}s{|~}y{}y~}|y~}y{|~}s{|~}y{}y~}u{|~}s{|~}vy|v{|~}ky~fy~}y{}y~9k~}n{"
3804       "}y~y{~|R{}l~ri~p{|q~}lq~m{|y~sy~|o{|y~sy~|m{}y~x{|y~|j{}y~yy~}o{|y~  Ey~}E{|Qj~j{|~y{|y~}l{|~}xy~|wy~u{|y~|v{|x"
3805       "~{|y~R{}y~a{|y~K{}~|M{}u~|M{|y~hy~}t{}y~i{|y~|f{}y~|_{}y~o{}n~}ey~}n{}y~t{|y~|j{}y~|jy~}t{|y~|e{}y~;{|}v~}jk~}k"
3806       "{|}v~}j{}~}n{|~}wy~ty~wy~t{|o~}o{|y~|t{|y~|px~e{|y~|qy~}p{|y~|e{|y~|gx~py~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|y{}y~}i{"
3807       "|y~|i{|y~|x{}w~wy~}s{|y~|w{|y~|{y~}qx~p{}y~|q{|y~|gx~p{}y~|r{|y~|v{|y~}c{|y~}jy~}k{}y~r{|y~|m{}y~y{}y~|o{}y~{|~"
3808       "}vy~{|y~|ox~y{|y~|gy~}h{|x~c{}~}a{}~|d{|y~| xy~|u{|y~m{}y~sy~}o{|y~e{|y~s{}~}o{|y~_y~|i{|y~s{}~}n{}y~t{}~}j{|y~"
3809       "d{|y~h{}y~|y~}d{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~s{}y~n{}y~sy~}p{|y~s{}~}k{}y~b{|}w~i{}y~g{}~}t{}~}ly~|y{}y~m{}~"
3810       "}{}~}y{|~}{}~}l{|w~|h{}~}y{}y~h{|y~}d{}y~|d{}~}d{|y~}|m{}t{|}x~}|V{~}w{|x~v{~|r{|y~ty~|l{|y~t{|y~|o{}y~v{}y~m{|"
3811       "y~s{}y~m{}y~}f{}y~t{}~}l{|y~y{}~}iy~|xy~}l{}~}t{}~}qy~}vy~}vy~}s{|y~x{|y~x{|y~|ny~|w{|y~|o{}y~x{|y~x{|y~|o{}y~s"
3812       "y~}p{|y~s{}y~iy~|j{|y~|ty~}o{|y~|ty~}k{|x~g{|y~|hx~q{|y~}r{}y~|x{}y~x{|x~r{|y~|ry~}o{}x~}x~}x~}px~p{}y~|t{}y~}]"
3813       "y~|^{|x~oy|yy~|y{|o{}y~|q{|x~oy|yy~|y{|M{}~}i{}y~w{~|h{|y~|f{}~}v{}~}n{|n~|S{}x~|{}~}Uy}y{}~}qy}p{|r~ky~}y{|y~}"
3814       "_{|~}Yy}x{}~}xy~|xy}8{}~| v{}~}t{}~}hy~y{}~| {{|r~iy~}y{|y~}jy~|w{|~|{|~}o{}~|s{|y~oy~v{|~|{|~}m{|y~j{|o~}o{|o~"
3815       "}o{|o~}o{|o~}o{|o~}o{|o~}s{|p~}jx~c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|qx~r{|y~|w{|y~|{y~}qx~p"
3816       "{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~|o{|x~|y~}my~}{}~}t{}y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~"
3817       "}i{|q~}m{}y~u{|x~|oy~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~ry~|u{|y~h{|y~e{|y~d{|y~d{|y~d{|y~_{|y~"
3818       "d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~U{|y~y{}~|x{}y~n{}~}t{}~}n"
3819       "{}~}t{}~}n{}~}t{}~}n{}~}t{}~}l{}~}y{}y~k{}y~sy~}m{}~}y{}y~:{|y~vy~|w{}~|s{|y~o{}~|s{|y~{|y~}y{|y~}{}~|s{|y~{|y~"
3820       "}t{}~|s{|y~o{}~|ky~f{}y~yy~}9k~}n{|y~y|~Q{|u~|y}u~r{}t~y}s~o{|s~}k{|s~|m{|y~sy~|ny~sy~ly~}wy~}j{|y~y|y~|ny~|  F"
3821       "x~ uj~j{|~x{}y~ly~wy~|wy~u{|y~|u{|x~}~}R{|y~a{}y~K{}~| r{}~}h{}y~t{}y~i{|y~|g{}y~|^{}y~o{}n~}ey~}n{}y~t{|y~|jy~"
3822       "}iy~}t{|y~|ey~}8{|}w~}|mk~}n{|}v~}|hx}m{~}wy~|v{|y~x{|~}t{}n~|p{|y~|t{|y~}p{}y~|f{|y~|r{}y~|p{|y~|e{|y~|g{}y~|q"
3823       "y~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|x{}y~}j{|y~|i{|y~|x{|x~}wy~}s{|y~|vy~}{y~}q{}y~|qx~p{|y~|g{}y~|qx~q{|y~|u{}y~|d{"
3824       "|y~}jy~}k{|y~|s{}y~|m{|y~|{y~}n{}y~{}~}v{}~y|y~|p{}y~|x{}y~gy~}hx~|c{}~}a{|~}d{|y~| y{|y~t{}y~m{}y~sy~|o{|y~|f{"
3825       "|y~|ty~}o{|y~|`y~|i{|y~|ty~}n{}y~t{}~}j{|y~d{|y~h{}y~{|x~e{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~|t{}y~n{}y~sy~|p{|y~"
3826       "|ty~}k{}y~_{|y~}j{}y~g{}~}ty~}l{}y~yy~}m{|y~{y~|y{|y~{y~}m{|y~y}y~h{|y~yy~|hx~by~}d{}~}d{}y~7{}~|y{|~}|~}x{}~q{"
3827       "|y~|v{|y~}l{|y~t{|y~|o{}~}v{}~}m{|y~|t{}y~my~|e{}y~t{}~}ky~|{y~|j{}y~w{}y~l{}~}ty~}qy~}vy~}vy~}s{|y~|y{|y~x{}y~"
3828       "my~|w{|y~|o{|y~x{|y~x{|y~|o{}y~sy~|p{|y~|t{}~}iy~|iy~}ty~|o{}y~s{}y~|lx~|g{|y~|h{|y~}rx~px~|y{}y~y{|x~|r{|y~|ry"
3829       "~}n{|r~}o{}y~|qx~r{}y~}^y~|_{|x~o{|y~|{y~|{}~}o{}y~|s{|x~o{|y~|{y~|{}~}Ny~}i{}y~w{~|h{|y~|f{|y~}|{|}y~|h{}y~L{|"
3830       "v~}T{|~xy~}|yx|x{~|U{}y~y{|y~}`{|~}Y{|~x{}~}x{|y~x{~|8{}~| v{}~}ty~}hy~y{}~| `{|y~}y{|y~|j{}~}v{~}y{|~}p{|y~ry~"
3831       "|p{}~|v{~}y{|~}mx~j{}n~|p{}n~|p{}n~|p{}n~|p{}n~|p{}n~s{}p~}j{}y~|d{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y"
3832       "~|k{|y~|r{|y~}r{|y~|vy~}{y~}q{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~o{|x~y{|y~}n{}y~}~}sx~r{|y~|s{}y~|q{|y"
3833       "~|s{}y~|q{|y~|s{}y~|q{|y~|s{}y~|jy~}i{|s~}|l{}y~sy~}p{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y"
3834       "~s{|y~t{}y~|i{|y~|f{|y~|e{|y~|e{|y~|e{|y~|`{|y~d{|y~d{|y~d{|y~i{|y~|t{}y~n{}y~t{}~}o{|y~|t{}y~o{|y~|t{}y~o{|y~|"
3835       "t{}y~o{|y~|t{}y~o{|y~|t{}y~ix|j{|y~|}~}w{}y~n{}~}ty~}n{}~}ty~}n{}~}ty~}n{}~}ty~}l{|y~yy~|k{}y~sy~|m{|y~yy~|9{}~"
3836       "}wy~|x{|y~q{}~}q{|y~q{}~}{~}w{|~y|y~q{}~}{~}t{|y~q{}~}q{|y~k{|y~f{|y~y|y~|9x|}y~}q|m{}~x}P{}w~}{}{v~|r{|t~y|}u~"
3837       "|n{}u~}i{|u~}l{|y~sy~|ny~|u{|y~ly~|w{}y~iy~y}y~m{|y~|  G{|y~| ry~|xy~|f{|~x{}y~m{}~|wy~|wy~ty~}t{|w~Q{|y~|b{}y~"
3838       "K{}~| ry~|h{|y~|uy~}i{|y~|h{}y~|]x~ns|x~y|e{|y~}n{|y~|u{}y~|k{|y~|iy~}t{}y~e{}y~4{|}w~}|U{|}v~}|Ty~w{}~}v{}y~xy"
3839       "~sy~}ry~}p{|y~|t{|y~}p{|x~p{|r{|y~|s{|x~o{|y~|e{|y~|g{|x~qy~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|w{}y~}k{|y~|i{|y~|wx~|"
3840       "wy~}s{|y~|v{|y~|y~}q{|x~r{}y~|p{|y~|g{|y~}r{}y~|q{|y~|u{|y~}d{|y~}jy~}k{|y~}sx~ky~}|y~|n{|y~|y~|v{}~y}y~p{|y~}w"
3841       "{|y~}hy~}i{}y~|b{}~}a{|y~d{|y~| y{|y~tx~m{}y~|u{|y~|ny~}ey~}u{|y~}ny~}`y~|hy~}u{|y~}n{}y~t{}~}j{|y~d{|y~h{}y~y{"
3842       "|x~f{|y~m{}y~ty~|u{|y~r{}y~t{}~}ny~}ty~}n{}y~|u{|y~|oy~}u{|y~}k{}y~^{}~}j{|y~g{}y~u{|y~}l{|y~y|y~|ly~|y~wy~|y~|"
3843       "mx~yy~}hy~y|y~hx~a{}y~d{}~}d{}~}6u~y{}y~}y~|py~|v{}y~}l{|y~t{|y~|o{}~}vy~|ly~}ty~}n{|y~|e{}y~t{}~}k{}~y}y~iy~}v"
3844       "y~|m{}y~ty~}qx~w{|x~w{|y~|ry~}y{|y~x{}~}my~|w{|y~|o{|y~x{|y~x{|y~n{}y~|u{|y~|oy~}ty~}iy~|i{}y~u{|y~ny~}s{|y~}m{"
3845       "}y~|f{|y~|g{}y~|t{}y~|p{|w~y}y~y}x~}q{|y~|ry~}ly}x~y}|n{|x~r{}y~|q{}y~}_y~|`{|x~mx~|y~|}y~|n{}y~|u{|x~mx~|y~|}y"
3846       "~|Ny~}i{|y~|x{~|h{|y~|fp~|i{}y~d{}~}d{|x~|S{~}x{}u~|y{}~S{}y~xy~}a{|~}X{~}y{|~|w{}~|{}~7y| u{}y~ty~}hy~y{}~| a{"
3847       "|y~}y{|y~|j{|y~v{}~x{|~}p{}~|s{}~|p{|y~v{}~x{|~}n{}y~|jy~}ry~}py~}ry~}py~}ry~}py~}ry~}py~}ry~}py~}ry~}ty~}ty~}j"
3848       "{|x~p{|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|rx~q{|y~|v{|y~|y~}q{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~"
3849       "|r{|x~r{}y~|r{|x~r{}y~|p{|x~w{|y~}o{|w~s{}y~|r{|y~}sx~p{|y~}sx~p{|y~}sx~p{|y~}sx~iy~}i{|y~w|h{}y~s{}~}p{|y~tx~n"
3850       "{|y~tx~n{|y~tx~n{|y~tx~n{|y~tx~n{|y~tx~s{|y~tx~}hy~}ey~}dy~}dy~}dy~}`{|y~d{|y~d{|y~d{|y~hy~}ty~}n{}y~t{}~}ny~}t"
3851       "y~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}j{|x~iy~}~}vy~}n{}y~u{|y~}n{}y~u{|y~}n{}y~u{|y~}n{}y~u{|y~}ky~y|y~j{}y~|u{|y"
3852       "~|ly~y|y~7y~}xy~|y{|y~|py~}s{|y~|py~}s{|y~|py~}s{|y~|py~}s{|y~|k{|y~ey~y}y~6{|y~}b{|x~|O{}y~}y{}{|}y~|p{|w~}{|}"
3853       "{}w~}l{}v~g{}w~}k{|y~sy~|n{}y~uy~}m{|y~v{|y~|j{}w~}l{}y~|  Gx~|u{}|Px|O{|y~x{|y~j{|w{|~x{}~}n{|y~v{}~|x{|y~t{}y"
3854       "~}t{}x~Py~|by~}K{}~|dx|Jx|f{|y~fx~v{}y~h{|y~|i{}y~|f{|s{}y~}f{}y~l{|t{|x~l{}y~ux~j{}y~h{}y~|v{|x~f{|y~}hx|dx|a{"
3855       "}v~}Xv~}|bx|m{}~|x{|y~}x{|x~{|y~|t{|y~|r{}y~p{|y~|t{}y~|o{}y~}s{|~|r{|y~|t{|x~|o{|y~|e{|y~|f{}y~}ry~}r{|y~|ry~}"
3856       "k{|y~|e{|y~|j{|y~|v{}y~}l{|y~|i{|y~|oy~}s{|y~|uv~}p{}y~}t{}y~}o{|y~|f{}y~}t{}y~}p{|y~|t{}y~|o{}r{}y~|jy~}j{}y~|"
3857       "u{|y~}k{}y~}y~ly~}y~u{|w~}px~u{|y~|iy~}j{}y~}a{}~}`y~|e{|y~| y{|y~|v{}x~m{}x~ux~m{}y~|f{}y~u{}y~}n{}y~|ay~|h{}y"
3858       "~u{}y~}n{}y~t{}~}j{|y~d{|y~h{}y~x{|x~g{|y~m{}y~ty~|u{|y~r{}y~t{}~}n{}y~|v{}y~|n{}x~ux~n{}y~u{}y~}k{}y~^y~}j{|y~"
3859       "g{|y~|v{}y~}ky~y}y~kw~}w{}w~m{}y~|y{|y~}i{}~}y~}i{}y~|a{}y~d{}~}d{}~}5{}y~}w{|y~}|o{}y~w{|w~l{|y~}u{}y~n{}~}w{}"
3860       "y~k{}y~|v{}y~|my~|e{}y~t{}~}k{|w~}j{}y~u{}~}m{}y~|v{}y~}q{}y~|x{}~}~|x{}y~q{}y~|{|y~y{|y~|my~|w{|y~|ny~}y{|y~x{"
3861       "}y~n{}x~ux~n{}y~|v{}y~|iy~|i{|y~}vy~}o{|y~|rx~n{|y~}e{|y~|fx~|v{}y~}n{|}q~|p{|y~|ry~}j{}y~j{}y~}t{}y~}o{}~}_y~|"
3862       "`{|y~ks~|l{}~|u{|y~ks~|My~}hx~x{~|h{|y~|gx~|}x~}|}y~|j{}y~d{}~}c{|x~S{|~}xy|}y|x{}~|R{}~|x{}~a{|}|X{|~}p{}~| /{"
3863       "}y~|v{}y~}hy~y{}~| a{|~|x{}~|i{}~|vr~|s{|~}s{}~|o{}~|vr~|q{}y~}j{|y~|r{}y~q{|y~|r{}y~q{|y~|r{}y~q{|y~|r{}y~q{|y"
3864       "~|r{}y~q{|y~|r{}y~u{|y~|ty~}i{}y~}s{|~|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|t{|x~}q{|y~|uv~}p{"
3865       "}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{|x~u{|y~}o{}y~}t{}y~}p{}y~|u{|y~}o{}y~|u{|y~}o{}y~|"
3866       "u{|y~}o{}y~|u{|y~}iy~}i{|y~|e{}y~sy~}p{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~s{|y~|v{"
3867       "}w~|i{}y~|f{}y~|e{}y~|e{}y~|e{}y~|a{|y~d{|y~d{|y~d{|y~h{}y~|v{}y~|n{}y~t{}~}n{}y~|v{}y~|n{}y~|v{}y~|n{}y~|v{}y~"
3868       "|n{}y~|v{}y~|n{}y~|v{}y~|j{|x~i{}y~}v{}y~|n{|y~|v{}y~}n{|y~|v{}y~}n{|y~|v{}y~}n{|y~|v{}y~}k{}~}y~}j{}x~ux~k{}~}"
3869       "y~}7{|y~}|{y|{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|j{|y~e{|w~|6y~}`x~I{|~hx|y{|}yw|jw~|"
3870       "f{}x~j{|y~sy~|mx~w|x~l{}~}uy~}j{}w~|k{}y~}|  I{|x~}|{y|y~|Py~}O{|~}x{|~}j{}~|y{|~{|}y~|n{}~|v{|y~x{}~}sx~}|y{|}"
3871       "u~Q{}~}by~|K{}~|d{}y~Jy~}f{}~}f{|y~}|{|}y~}kw|y~w|m{}y~}s|my~}w|}w~e{}y~ly~}w|}x~}l{|x~|y{|x~|jy~}h{|x~}|{y|x~|"
3872       "m{~}w|}y~}g{}y~d{}y~_{|}y~}Xy~}|_y~}m{|~}w{|u~y}w~|sx~q{|y~|q{|y~t|}y~}m{}x~}v|}y~|r{|y~u|y}x~}n{|y~q|n{|y~|e{}"
3873       "x~}v|}x~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|u{}y~}m{|y~q|r{|y~|oy~}s{|y~|u{|w~}o{}x~}|{y|}x~n{|y~|e{}x~}|{y|}x~o{|y~|t"
3874       "{|y~}oy~}x|{y|x~}iy~}j{|x~}w|}x~j{|w~}l{}x~}tw~|q{}y~|t{}y~iy~}k{|x~o|l{}~}`{}~}e{|y~| xx~|y{|}w~m{}w~|y{|x~|lx"
3875       "~}|yy|}|mx~|y{|}x~}m{}y~}|x{|}~}jy~|h{|x~|y{|}x~}n{}y~t{}~}j{|y~d{|y~h{}y~w{|x~h{|y~m{}y~ty~|u{|y~r{}y~t{}~}mx~"
3876       "|y{|}y~}m{}w~|y{|x~|mx~|y{|}x~}k{}y~g{}~}|x{|}y~|j{|y~}gx~|y{|}x~}k{}w~}k{}x~}w{|x~}n{|y~|w{}y~|j{|w~|j{}y~|`{}"
3877       "y~d{}~}d{}~} w{|y~}|{|y~}y~}m{|x~}|y{|}y~}n{|~}x{|y~|jx~|y{|}y~}l{}y~}|x{|y}m{}y~t{}~}jw~|jy~}u{|y~|n{}x~}|{|}w"
3878       "~y|s{|x~|{|y~|~}|{}y~}px~y|y~|}y~}ly~|vy~}n{}y~|{|y~y{}y~|n{}w~|y{|x~|mx~|y{|}y~}h{}y~|i{}y~}y{|x~nx~q|}y~|ox~r"
3879       "|m{|y~|iw|x~}x{}y~}w|o{|y}x~y}|n{|y~|ry~}j{}y~i{}x~}|{y|}x~m{|^y~|_{|iu~|j{|s{|iu~|Ly~}h{|y~}|{~|{|}mx|y~}t|o{|"
3880       "y~r{}~}j{}y~d{}~}b{|y~|S{|~}r{}~|Py|w{}:{|~}r{}~|=k|!{}x~}|{|}w~y|jy~y{}~| ay|w{}h{|~}uv|}~}|ry~s{}~|o{|~}uv|}~"
3881       "}|q{|y~}ix~q{|y~|rx~q{|y~|rx~q{|y~|rx~q{|y~|rx~q{|y~|r{}y~q{|y~|vx~sy~}r|q{}x~}v|}y~|p{|y~q|n{|y~q|n{|y~q|n{|y~"
3882       "q|j{|y~|e{|y~|e{|y~|e{|y~|k{|y~}u|}x~}p{|y~|u{|w~}o{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y"
3883       "|}x~ox~s{|y~}pv~}|{y|}x~o{|x~}w|}x~n{|x~}w|}x~n{|x~}w|}x~n{|x~}w|}x~hy~}i{|y~|e{}y~{x|y{|}y~|ox~|y{|}w~mx~|y{|}"
3884       "w~mx~|y{|}w~mx~|y{|}w~mx~|y{|}w~mx~|y{|}w~rx~|y{|}y~|}y~}|x{|}~}qx~}|yy|}|m{}y~}|x{|}~}m{}y~}|x{|}~}m{}y~}|x{|}"
3885       "~}m{}y~}|x{|}~}j{|y~d{|y~d{|y~d{|y~gx~|y{|}y~}m{}y~t{}~}mx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}"
3886       "i{|x~i{|x~|y{|}y~}lx~|y{|}x~}mx~|y{|}x~}mx~|y{|}x~}mx~|y{|}x~}k{|w~|j{}w~|y{|x~|k{|w~|6{|q~|m{|q~|m{|q~|m{|q~|m"
3887       "{|q~|i{|y~dw~5{}~_{}~}I{}~c{}~d{|y~|dy~|j{|y~sy~|m{|s~|ly~}u{}~}j{|w~i{}m~  S{|s~}Oy~}O{}~|x{}~|j{}q~}n{|~}t{}y"
3888       "~}x~q{}s~}{|y~}R{|y~c{|y~J{}~|dx~Jy~}fy~|e{}t~}k{}q~}no~|nq~}d{}y~lq~|j{|s~|j{|y~|g{|r~|ls~}f{}y~dx~\\y|X{}|]y~"
3889       "}ly~|w{|}y~}|{}~}|r{|y~}py~}q{|p~}k{|q~}q{|p~}|m{|o~|o{|y~|d{|p~}q{|y~|ry~}k{|y~|e{|y~|j{|y~|t{}y~}n{|o~r{|y~|o"
3890       "y~}s{|y~|t{}x~}n{}r~}m{|y~|d{}r~}n{|y~|s{}y~|pp~}hy~}i{|r~}hw~|l{}x~}tw~|r{|y~}s{|y~}jy~}k{}l~|m{}~}`{|y~e{|y~|"
3891       " x{|t~}|y~m{}y~|t~|j{}s~|m{|t~|}~}l{}r~}jy~|g{|t~|}~}n{}y~t{}~}j{|y~d{|y~h{}y~v{|x~i{|y~m{}y~ty~|u{|y~r{}y~t{}~"
3892       "}m{|s~}l{}y~|t~|l{|t~|}~}k{}y~g{}r~}h{}u~k{|t~|}~}k{|w~|k{|x~|w{|x~|ny~}u{}y~iw~io~h{}y~d{}~}d{}~} v{}t~{}x~}o{"
3893       "|q~}ly~}y|y~}i{|s~}j{}s~}m{}y~t{}~}j{}x~j{}y~sy~}n{}~}t~}x~}r{|u~|{t~o{|q~ky~|vw~}ot~}x~}m{}y~|t~|l{|s~}g{|v~j{"
3894       "}t~|o{|k~}p{|o~|n{|y~|is~y{|t~}l{}y~k{|y~|ry~}j{}y~h{}r~}Ny~|Kw~|Lw~|Ky~}g{|r~n{|o~}n{|p{|i{}y~d{}~}ay~|R{|y~}y"
3895       "|{y|}y~| a{|y~}y|{y|}y~|<k~}\"{}~}t~}x~}jy~y{}~| Gy~o{|~}r{}~|ty~|ny~o{|~}q{|y~}i{|y~}py~}s{|y~}py~}s{|y~}py~}s"
3896       "{|y~}py~}s{|y~}py~}s{|y~}py~}w{|y~|so~}q{|q~}o{|o~|o{|o~|o{|o~|o{|o~|k{|y~|e{|y~|e{|y~|e{|y~|k{|o~|o{|y~|t{}x~}"
3897       "n{}r~}l{}r~}l{}r~}l{}r~}l{}r~}n{|~q{|~p{}~y|r~}m{|r~}l{|r~}l{|r~}l{|r~}gy~}i{|y~|e{}y~{}t~}n{|t~}|y~m{|t~}|y~m{"
3898       "|t~}|y~m{|t~}|y~m{|t~}|y~m{|t~}|y~r{|s~|y{}r~|p{}s~|l{}r~}l{}r~}l{}r~}l{}r~}j{|y~d{|y~d{|y~d{|y~fs~}l{}y~t{}~}m"
3899       "{|s~}k{|s~}k{|s~}k{|s~}k{|s~}Rq~}k{|t~|}~}m{|t~|}~}m{|t~|}~}m{|t~|}~}jw~i{}y~|t~|iw~3{|}w~}|i{|}w~}|i{|}w~}|i{|"
3900       "}w~}|i{|}w~}|g{|~}d{}y~} r{|~|Iy~|dy~|d{|}cy|i{|y}sy}|k{}w~}k{|y~|u{|y~ix~}gy}p~  Q{|}x~}|Ny~}Oy~wy~h{|y}v~}|my"
3901       "~r{|x~}o{|}w~}|x{}y~}Ry~|d{}~}J{}~|dy~}Jy~}g{|y~c{|}x~}|j{}q~}no~|m{|}w~y}|c{}y~l{|y}w~y}f{}w~}hx~dy}x~y}|k{|}w"
3902       "~}|e{}y~dy~} ry~}l{|y~c{}y~|p{}y~q{|r~}|h{|}w~}|o{|s~y}|k{|o~|o{|y~|b{|}w~y}|o{|y~|ry~}k{|y~|e{|y~|j{|y~|s{}y~}"
3903       "o{|o~r{|y~|oy~}s{|y~|t{|x~}l{|}x~y}|l{|y~|b{|}v~}m{|y~|ry~}o{|y}w~y}|gy~}g{|}w~}|g{|x~k{|x~|t{}x~qx~q{}y~|ky~}k"
3904       "{}l~|m{}~}_y~|f{|y~| v{}x~}|{|y~m{}y~{|}x~}|h{|}x~y}|j{}x~}|{}~}k{|}w~y}|iy~|e{}x~}|{}~}n{}y~t{}~}j{|y~d{|y~h{}"
3905       "y~u{|x~j{|y~m{}y~ty~|u{|y~r{}y~t{}~}k{|}x~}|k{}y~{|}x~}|i{}x~}|{}~}k{}y~f{|y}w~}|fy}w~j{|}x~}y{}~}jx~}ix~ux~|o{"
3906       "}y~t{|y~}j{|y~}io~h{}y~d{}~}d{}~} u{|}x~}|y{}y~}o{|y~|}w~}|k{|v~}f{|}x~}|h{|}w~y}|m{}y~t{}~}j{|y~|jy~}s{}y~n{}~"
3907       "}{}x~}y{}~}|q{|}y~}|x{}x~}l{}v~}|jy~|v{|}y~}n{}s~|l{}y~{|}x~}|i{|}x~}|e{|}x~i{|}x~}m{}j~p{|o~|n{|y~|is~y{|t~}l{"
3908       "}y~k{|y~|ry~}j{}y~f{|}x~y}|My~|Jy~|Jy~|Jy~}f{|}u~}n{|o~}O{}y~d{}~}h{}|w{}y~O{|}v~}| ]{|}v~}|:k~}\"{}~}{}x~}y{}~"
3909       "}|jy~y{}~| H{}~|o{|~|s{|~}t{|t~|t{}~|o{|~|q{}y~h{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}"
3910       "y~w{}y~ro~}o{|}w~}|m{|o~|o{|o~|o{|o~|o{|o~|k{|y~|e{|y~|e{|y~|e{|y~|k{|s~y}|m{|y~|t{|x~}l{|}x~y}|i{|}x~y}|i{|}x~"
3911       "y}|i{|}x~y}|i{|}x~y}|U{|~}x{|}x~y}|j{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|fy~}i{|y~|e{}y~{|}w~}|k{}x~}|{|y~k{}x~}|{|y~"
3912       "k{}x~}|{|y~k{}x~}|{|y~k{}x~}|{|y~k{}x~}|{|y~p{}x~}|v{|}w~y}|n{|}x~y}|j{|}w~y}|j{|}w~y}|j{|}w~y}|j{|}w~y}|i{|y~d"
3913       "{|y~d{|y~d{|y~e{|}x~}|k{}y~t{}~}k{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|R{}~|{|}x~}|i{|}x~}y{}~}l{|}x~}y{}~}l{|"
3914       "}x~}y{}~}l{|}x~}y{}~}j{|y~}i{}y~{|}x~}|h{|y~}  e{|~} V{|      .{|~ p{}~}dy~|1{|y~2{}~}   U{|y~ ^{}~}  ){|y~|  {"
3915       "}y~}  6{}~}_{}~}f{|y~|  -y~}6{|y~ A{}y~Z{}~}  j{|y~I{}y~d{}~}d{}~} \\{|y~a{|}y|*{}~}j{|y~|O{}~}F{|y~K{|}y~y|j{}"
3916       "y~     Ry~}c{|~| r{}~}h{}t~|K{| U{| '{}~}^y~y{}~|O{|~| wy|cy}|st|sy|ay~}  ^{|~|    9{|    Y{|~|    8y| Q{|y~h{}"
3917       "y~`{|y~  d{|~}        c{|~ oy~e{}~}0{}~}2y~|   U{}~} ]{}y~|r{|y}  7{}y~  y{}y~|  7{}~}_{|y~f{|y~|  .{|y~|6{|y~ "
3918       "A{}y~Z{}~}  j{}~}I{|y~d{}~}dy~} \\{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~h{}y~     Ry~}b{~| r{}~}h{|y}x~}|   ){}~}^y~y{"
3919       "}~|N{}~ 'y~}  ]y~         p{~}      g{}~}h{}y~`{}~}  h{|}|{}~}        c{|~ o{}~}fy~/{}~    c{}~ [{}y~}|v{|}y~} "
3920       " 7x~  x{}y~|  8{}v~M{|v~|  .{}y~5{}~} A{}y~Z{}~}  k{|y~|I{|y~}e{}~}e{|y~| \\{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~h{}y"
3921       "~     Ry~}b{~| r{}~}    i{}~}*{}~| (x~uy|  e{}~|         q{}~      h{|y~|h{}y~a{|y~|  h{|y~|y~|        c{|~ ny~"
3922       "g{}~}      M{|}q~|  9y|}y~|    1{}w~}M{|v~|  6y}|x{|}y~|6{|y~} A{}y~Z{}~}  l{|x~G{}w~}h{}~}h{}w~} [{|y~ g{}~}j{"
3923       "|y~|O{}~}F{|y~J{|y~h{}y~     Ry~}b{~| r{}~}    i{}~}-x}y~ '{}x~w|y~|  e{}~|         qy~      i{|x~g{}y~b{|x~  f"
3924       "{}w~                e{|y}w~}|  8{|w~}     ({|n~}  m{}s~}7{|w~ @{}y~Z{}~}  nv~|F{|y}~}h{}~}h{}~y}| Z{|y~ g{}~}j{"
3925       "|y~|O{}~}F{|y~J{|y~h{}y~     Rx| W{}~}    i{}~}-{}y~}| &{}s~|  i{|~y}y~         t{|~y}y~      kv~|g{}y~dv~|  ex"
3926       "}                   s{|y~}|     '{|n~}  m{|y}w~}|6{|y~} ?{}y~Z{}~}  nx~}|-{}~} B{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~"
3927       "h{}y~           q{}~}  -{|}x~}|  f{}y~}|         t{|x~}|      kx~}|f{}y~dx~}|                                  "
3928       " :{}~}                                     I{" };
3929 
3930     // Define a 52x64 font (large sans).
3931     static const char *const data_font_large[] = {
3932       "                                                                                                               "
3933       " -{|                                                                                                           "
3934       "                                    [{|x}|Dw}|Pw}| @{}v~} C{|w}|Ew}|Pv}| xv|Ev|Pu|  kv|Dw|P{|v}  6{|w}|E{|x}|P{"
3935       "|w}| pw}|                                                                                                      "
3936       "                           G{|w~}F{}w~P{}v~}Q{}w~}|w{|x~X{|v~vv~|U{|r~| D{}w~F{}w~P{}u~S{|v~|wv~}V{}w~|G{|w~|Q{"
3937       "|u~|Sv~}w{}w~}\"{|}x~}|v{|x~W{}w~|F{}w~Q{|u~}Q{}x~}|v{|x~X{}w~}w{|v~ G{}w~F{}w~|Q{}u~Rv~|w{}w~}O{}w~           "
3938       "                                                                                                               "
3939       "       E{|w~|H{}w~P{}t~}Ss~}|y{}x~X{|v~vv~|V{|p~ Cw~}H{|w~|Q{|t~}T{|v~|wv~}U{}w~Gw~}Q{|s~|Tv~}w{}w~}#{|s~}|{|x~"
3940       "}V{}w~|H{}w~Ps~}St~}w{}y~}X{}w~}w{|v~ Fw~}H{|w~|Q{|t~}Sv~|w{}w~}P{|w~|                                         "
3941       "                                                                                        D{|w~|J{|w~|Q{|x~}{w~|U"
3942       "{}l~}X{|v~vv~|Vw~}x{}x~} D{|w~|J{|w~|Q{|w~{}x~}U{|v~|wv~}T{}x~}Iw~}Pw~y|w~Tv~}w{}w~}#k~|U{}w~I{|w~|Q{}x~|{}x~|U"
3943       "{}r~|{|x~}X{}w~}w{|v~ Ew~|Iw~}Pw~|}x~}Tv~|w{}w~}Q{|w~|    M{|                                                  "
3944       "                                                                           q{}w~Jw~|Q{|x~}xw~Ux~}y{|}t~}W{|v~vv"
3945       "~|W{|x~|v{|x~| D{|w~|Kw~}Pw~x{}x~|V{|v~|wv~}S{}x~}K{}x~}P{}x~|y{|x~}Uv~}w{}w~}${|x~|y{|s~}S{}x~}K{|w~|Q{}x~}xw~"
3946       "Ux~}{|}r~W{}w~}w{|v~ E{|w~|K{}x~}Pw~|y{}x~|Uv~|w{}w~}Qw~|    O{}v~}                                            "
3947       "                                                                                 s{}x~}L{}x~|Pw~vw~W{|x~v{|}w~}"
3948       "V{|v~vv~|W{}y~}tx~} C{|w~L{}x~}P{}x~|w{}x~|W{|v~|wv~}R{}x~|M{}x~}P{}x~|w{|x~}Vv~}w{}w~}${|x~v{|}w~|Q{}x~}Lw~|Q{"
3949       "|x~}vw~W{|x~w{|t~|W{}w~}w{|v~ D{|w~L{|x~}P{}x~|w{}x~|Vv~|w{}w~}R{}x~}    P{|r~|                      Y{}w~|    "
3950       "                                                                                                  A{}x~|N{}x~}P"
3951       "{}x~u{|x~}\"v|vv|V{}y~}t{}y~} B{}x~}N{|x~}P{}x~|u{}x~Vv|vv|P{}x~|O{|x~}P{}x~|u{|x~}Wv|uv| D{}x~|N{}x~|Q{|x~}tx~"
3952       "}X{|x~u{|}y~}|Vu|vv| C{|x~}N{|w~P{|x~|u{}x~Vv|vu|S{|x~}    Op~|                      Zv~|                      "
3953       "                         ;v~                                                u{|v~      6{|y}|N{|y}|P{|x}s{|x} I"
3954       "{}y~}t{}y~} Aw|Nw|Ow|sw|    Qw|Nw|Pw|rx|  5{|x}N{|x}O{|y}|s{|y}| {{|y}| Dv|@v|Rv| C{}x~}x{|w~ Hu|@v|Rw| yv}@v}R"
3955       "{|w}  lv|@v|Rv|  8v}@v}|S{|w} m{}w~|        E{|y~x}|                                               ;{|w~}      "
3956       "                                          vv~|         J{}y~}t{}y~}               e{}w~}B{|w~}Rv~| Dx~|v{|x~| H"
3957       "v~A{}w~|S{|w~} {{|w~}B{}w~|S{|v~| Ay|sx|Y{}w~|B{|w~}Rv~  8{|w~}B{|w~}Rv~| o{|w~}     ?y}~}|  *x~             J{"
3958       "|y~|  b{}x~|T{|x~}                            L{|q~} y{}q~| H{|w~} xw~} `{|w~| {{|}t~)w~}Cv~Lv~Tw~}Dv~        G"
3959       "{|x}w~}Tw~|U{|v~y}|  1{|y}v~y}|   cv~y}     p{|y}x~y}|             {{v|vv|      3{}w~|         I{|x~|v{|x~|    "
3960       "      %{|   5{|y}w~y}|U{}w~|Cv~R{}v~}Q{|}y~}|ux~|Y{|v~|wv~}W{|x~t{}y~} H{|w~}C{}w~|Ru~|S{}w~}w{|v~W{}w~|D{|w~}R"
3961       "t~S{|v~vv~|X{|v~}K{}w~}ux~X{}w~C{|w~}R{}v~}Q{|}y~}|ux~|Y{|v~vv~| J{|w~}D{|w~}R{}u~Rv~|w{}w~}N{|w~}Zw~}Hv~}w{}w~"
3962       "}    N{|u~}  ,{|y~} Ix|Tx|kw|        Ru|  6{|y~|Yv|fx}|Zu| o{|w~Rw~|Hx|   Xu| vt|Ns| =t| xt|Ot|   [u|  ds|  kr|"
3963       "    Qt| ut| ts|    S{|q~} y{}q~| G{}w~| yw~} `{|w~|!{}q~)w~}Cv~Lv~Tw~}Dv~        I{|r~}Tw~|U{|r~}  5{|}o~| yr| "
3964       " ps~}     t{|p~|  kt|  is| s{|y} r{|x}| rx}|  bt|  lu|S{|v~vv~|!{|y}w~y}|  :{|l~|Qx| u{|y}w~}|Q{|x}w~y}|K{|w~| "
3965       " 9y|y}w~|O{|y}w~}|)y|x}dw~|hy|x}dw~|ly|x}y~y}|e{}x~|   6w~}x{}x~} us|      lt|Nt|Nt|Nt|Nt| ut|p{}~|   9{|}o~|V{"
3966       "}w~D{}w~R{|t~|S{|u~}vx~|Y{|v~|wv~}W{}y~}t{|x~ G{|w~}E{|w~}R{}t~S{}w~}w{|v~V{}w~E{|w~}R{}t~}T{|v~vv~|W{|v~}s{|y}"
3967       "X{}u~}w{|x~Ww~}Dv~R{|t~|S{|u~}w{|x~X{|v~vv~| I{}w~|Ew~}R{|t~}Sv~|w{}w~}Nw~}Yw~}Hv~}w{}w~}    O{|s~|cW}  i{}y~|"
3968       "\"{|}L{|u~}|Z{|}v~}|p{}u~}V{|}  /g|    ({}r~}| v~}R{}x~}vw~}R{|x}|t{|x}|V{|y~|\\{|}t~|i{}x~|]{}q~}|O{}x~}Iw~|R{|"
3969       "w~Hx~  *{|w~V{|}s~}|Sy|y}v~}T{|}q~}|V{|y}p~}|L{|u~}\\{|g~}T{}q~y}|_{}c~}[{|}q~}|U{|}r~}|   b{|}q~| w{}v~}X{}k~y"
3970       "}|R{|y}p~}|b{}m~x}y|W{}c~|`{}e~Y{|}o~}|a{}w~}hv~|Y{}w~}M{}w~}W{}w~}k{|u~}b{}w~}V{}t~|h{}t~|h{}u~}jv~^{|}p~}|Z{}"
3971       "m~y}|U{|}p~}|\\{}m~y}y|S{|}o~}y|bZ~g{|v~h{}w~}i{|v~|d{|v~|rv~|l{|v~}kv~|p{|v~|i{}v~g{}v~fv~}g\\~]{|q~}Uw~}I{}q~"
3972       "|P{|w}| w{}w~ yw~} `{|w~|\"o~)w~}Cv~Lv~Tw~}Dv~        J{|q~}Tw~|U{|q~}  7{}l~}\"y}p~y}  sr~}     v{}n~}R{}v~}V{"
3973       "}c~|_{}d~}^{|}p~}|R{|v~Y{}^~|iv~}r{|v~qv~}a{|}p~}| x{}x~} s{}w~ s{}w~|  f{|}r~}|-{}w~|i{|v~({|q~}|W{|v~vv~|Ty|u"
3974       "}y|U{|}o~|  ly|u}y|U{|l~|T{|}v~}| {|}p~|T{}p~}|N{|w~} yy|}m~} N{|r~|P{}q~|0{|y}t~|f{}x~}l{|y}t~|f{}x~}l{}p~}h{}"
3975       "x~}%{}v~}N{}v~}N{}v~}N{}v~}N{}v~}Q{|p~W{}\\~}b{|y}p~}|^{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|"
3976       "Z{}u~}jv~^{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|\"{|}q~y}t{}x~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}g{}"
3977       "v~fv~}c{}w~}J{|l~}Vw~}F{}w~|R{}x~|w~Ss~}x{|x~X{|v~|wv~}W{}y~}t{|x~ F{|w~|Fw~}R{|x~y}x~}T{}w~}w{|v~U{}x~}Fw~}R{|"
3978       "w~{w~|U{|v~vv~|V{}v~|x{|}v~Y{|s~}x{}x~W{|w~}F{}w~Qw~|w~Ss~}x{|x~X{|v~vv~| H{}w~F{}w~Qw~|}x~|Tv~|w{}w~}O{}w~Xw~}"
3979       "Hv~}w{}w~}    P{|q~c{}Y~}  ix~!y~|N{}r~}\\{}r~}s{|q~|Y{|y~}  5{|}e~}    *{}m~|\"v~}R{}x~}vw~}Rw~|tw~|V{|y~|]{}q"
3980       "~}k{|w~^{|l~|Q{}x~}J{}w~P{}x~}Ix~  *{}x~}W{}n~|Zy|}p~}W{|}k~}Z{}i~|Nt~}\\{|g~}V{}l~|`{}c~}\\{}l~|X{}n~}   e{|l~"
3981       "|Ty|y}u~y}|Rt~X{}g~}V{|}j~}d{}g~}|Z{}c~|`{}e~\\{|}i~}|d{}w~}hv~|Y{}w~}M{}w~}W{}w~}l{|u~}a{}w~}V{}t~}i{|s~|h{}t~"
3982       "|kv~`{|k~}|\\{}i~}|Z{|k~}|^{}i~}|W{|h~}dZ~g{|v~h{}w~}hv~}d{}v~q{}w~}l{}u~kv~|o{}v~j{|v~|fv~}h{}v~f\\~]{|v~u}U{}"
3983       "w~Iu}v~|Qt~| w{}x~} {{w~} `{|w~|#{}o~)w~}Cv~Lv~Tw~}Dv~    Ov|    s~x}|Tw~|U{|x}s~|  9{}j~}%{}j~|  uq~|     x{}l"
3984       "~}St~V{}c~|_{}d~}`{|}k~|T{|v~Y{}^~|iv~}r{|v~qv~}c{|k~}| {}v~} t{}w~ t{}u~|  i{|l~-v~i{}w~|Xw}|R{|l~X{|v~vv~|W{|"
3985       "}o~}|X{|m~|  p{|}o~}|X{|l~|U{}r~}!{|n~}U{}n~|Ow~} {{|}j~} N{|r~|R{|n~}1{|r~|g{|w~k{|r~|g{|w~k{}n~iw~$t~Nt~Nt~Nt"
3986       "~Nt~P{|r~V[~}d{|}j~}`{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}t~|kv~`{|k~}|Z{|k~}|Z{|k~}|Z{|k~}"
3987       "|Z{|k~}|&{|k~}w{|w~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}fv~}h{}v~b{}w~}K{}j~}W{|w~|H{|w~|R{|x~}{|x~}U{|"
3988       "x~}|w~}|{}x~X{|v~|wv~}W{|x~t{}y~} E{}w~G{}x~}Qw~|{}x~|U{}w~}w{|v~Tw~}H{}w~Q{}x~|{|x~}U{|v~vv~|U{}v~|}t~}Y{}x~|{"
3989       "}w~y|x~}V{|w~|H{|w~|R{}x~|{|x~}U{}x~}|w~}{|x~}X{|v~vv~| G{}x~}H{|w~|R{}x~|yw~Tv~|w{}w~}P{|w~|Xw~}Hv~}w{}w~}    "
3990       "P{}w~y|w~|d{|Y~|  j{|y~}\"{}x~Oo~}_{|o~}u{|o~}Zw~|  8{}b~}    ,{|j~}#v~}R{}x~}vw~}Rw~sw~U{|y~|^{}o~}lw~|_{}k~|Q"
3991       "{}x~}Jw~|P{|w~|Jx~  *w~|Xk~|[m~}X{}h~}[{}h~}P{}t~}\\{|g~}X{|j~|`{}c~}^{|i~}[{|j~   gi~|X{|}l~}|V{}t~|Y{}e~|Y{}f"
3992       "~}f{}d~}\\{}c~|`{}e~]{}e~}|f{}w~}hv~|Y{}w~}M{}w~}W{}w~}m{|u~|`{}w~}V{}s~|j{}s~|h{}t~}kv~b{|g~}]{}g~}]{|g~}_{}g~"
3993       "}Y{}f~dZ~g{|v~h{}w~}h{}v~dv~}q{}w~}lt~|m{|v~mv~}kv~}e{|v~|j{|v~|f\\~]{|w~}O{|w~|D{|w~|Rr~| ww~} w~} `{|w~|${|v~"
3994       "}|#w~}Cv~Lv~Tw~}Dv~    Ov~   !{|v~}Nw~|O{|v~}  :{|u~}|w{|}v~|'{}i~|  r{|}v~}     y{}v~}|x{|}v~}U{}t~|W{}c~|_{}d"
3995       "~}a{}g~|V{|v~Y{}^~|iv~}r{|v~qv~}e{|g~}\"{}t~} u{}w~ u{}s~| >y~}P{|k~-{|w~}k{|w~}Ww~|S{|k~X{|v~vv~|Y{|}k~}|Z{|y~"
3996       "}y|xy|}w~|  s{|}k~}|Z{|l~|V{}p~}\"{|y~}|w{|}w~|V{|}|u{|v~P{}x~} {{}h~} N{|~y}y|}x~|S{|v~}|y{|}w~}2{|w~y}x~|g{}x"
3997       "~|k{|w~y}x~|g{}x~|kx}|w{|}w~}k{}x~}%{}t~|P{}t~|P{}t~|P{}t~|P{}t~|P{}t~}W{|[~}e{}f~}b{}c~|a{}c~|a{}c~|a{}c~|X{}w"
3998       "~}M{}w~}M{}w~}M{}w~}Z{|d~}|`{}t~}kv~b{|g~}]{|g~}]{|g~}]{|g~}]{|g~}){|g~|{|w~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f"
3999       "{|v~h{}w~}f{|v~|j{|v~|b{}w~}L{|u~}|w{|}v~|W{|w~|Iw~}Qw~x{}x~|V{}y~}x{}s~|X{|v~|wv~}Vx~}v{|x~| D{}x~}I{}w~Q{}x~|"
4000       "xw~U{}w~}w{|v~T{|w~|J{|w~Q{|x~}x{|x~|V{|v~vv~|T{}q~}|Wx~|x{}s~T{|w~I{|w~|R{|x~}x{}x~|Vx~}x{}s~|X{|v~vv~| Fw~}J{"
4001       "|w~|R{|x~}x{|x~}Uv~|w{}w~}Q{|w~|Ww~}Hv~}w{}w~}    Pw~}y{|x~}cY~  i{}y~|#{|w~}Qm~|`m~}w{|m~|\\{}v~|  ;{}`~}    -"
4002       "{|r~x}t~}$v~}R{}x~}vw~}S{|w~t{|x~}U{|y~|_{|w~}w{}w~|n{}x~}_{|t~w}u~|Q{}x~}K{}w~N{}x~}Jx~  +{|w~Xs~y}s~|\\m~}X{}"
4003       "f~\\{}g~}R{|s~}\\{|g~}Y{|i~|`{}c~|_{|s~w}s~}]{|s~x}s~   hr~}r~|[{|f~}Xs~}Y{}d~|\\{|c~}g{}b~|^{}c~|`{}e~_{|a~|g{"
4004       "}w~}hv~|Y{}w~}M{}w~}W{}w~}n{|u~|_{}w~}V{}s~}jr~|h{}s~|lv~c{|p~}q~}^{}f~}_{|p~}q~}`{}e~[{}q~}p~dZ~g{|v~h{}w~}h{|"
4005       "v~|f{|v~p{|v~m{|t~}m{}w~}m{|v~|m{}v~c{}v~jv~}e\\~]{|w~}Nw~}D{|w~|Sp~| ww~|!w~} `{|w~|${}w~}!w~}Cv~Lv~Tw~}Dv~   "
4006       " Ov~   !{}w~}Mw~|N{|v~  :{}v~|s{|v~V{|t}|V{|t~s}w~|  p{|v~     {{|v~|t{|v~|Vs~}W{}c~|_{}d~}c{|d~|W{|v~Y{}^~|iv~"
4007       "}r{|v~qv~}f{|p~}q~}${}r~} v{}w~ v{}q~| ?y~}Ps~x}u~,v~k{}w~|Ww~|Su~}v|}w~X{|v~vv~|Z{}v~}y|wy|}v~}[{|}q{}x~}  t{}"
4008       "v~}y|wy|}v~}&{}w~|x{|w~}#y|r{}x~}Kw~|R{|w~ {{}p~}v|x~} H{}x~|S{}w~t{}w~|3x|x{}x~|h{|x~}j{|}|x{}x~|h{|x~}`{|w~l{"
4009       "|w~$s~}Ps~}Ps~}Ps~}Ps~}Pr~W{}[~}g{|c~}c{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}s~|lv~c{|p~}q~}_"
4010       "{|p~}q~}_{|p~}q~}_{|p~}q~}_{|p~}q~}+{|p~}q~}w~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}e{}v~jv~}a{}w~}Lu~r{"
4011       "|v~V{|w~J{}x~}Q{}x~|w{}x~Vx~|w{}u~}Vv|vv|U{}x~}x|}w~ Bw~|K{|w~|R{|x~}w{|x~}Vu|vv|S{|w~K{|w~|Qx~}v{}x~Uv|vv|T{|}"
4012       "t~}|Tx~|w{|u~|S{}x~}Jw~}Qw~vw~Vx~|w{}u~}Vv|vv| Dw~|Kw~|Qw~v{}x~|Vv|vv|Pw~|Vw~}Hv|uv| G{|t}|P{|t}|P{|t}|P{|t}|P{"
4013       "|t}|Lw~|xw~c{|[~}  iy~}\"u~|S{|l~a{}l~|x{}l~]{}t~  ={|^~}    .{|u~}|u{|}w~}$v~}R{}x~}vw~}S{}x~}t{}x~}Xy|y}y~y}x"
4014       "|cw~}u{}w~o{|w~^u~}t{|}y~|Q{}x~}Kw~|N{|w~|T{}sx~s{}  4{}x~}Y{}v~}|v{}u~\\m~}X{}v~y}|wy|s~]{}x~}x|v{|}t~}Sr~}\\{"
4015       "|v~k|Z{|t~}|v{|y}y~|`h|u~^t~|u{|}u~|^u~}|v{|}v~}   iv~y|v{|t~]{|o~y}p~|[{|r~|Z{}w~}q|}s~]{|s~}|t{|}u~}g{}w~}r|y"
4016       "}q~}_{}w~}h|_{}w~}j|`{|s~}|s{|}t~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}o{}u~|^{}w~}V{}r~k{|r~|h{}r~lv~d{|t~}|uy|s~_{}w~}"
4017       "s|y}t~}a{|t~}|uy|s~a{}w~}s|y}s~]{}u~}|ty|}v~dn|}v~}n|g{|v~h{}w~}gv~}f{}w~}ov~|n{|t~}mv~|l{}v~|o{|v~|bv~}l{}v~dc"
4018       "|u~}]{|w~}N{}w~D{|w~|T{}o~| x{|w~!w~} `{|w~|${}w~ w~} >w~}Dv~    Ov~   !{}w~|Mw~|M{}w~  :v~|q{}w~|Xp~}X{}v~|p{|"
4019       "}|  o{}w~|     v~|r{|v~W{|r~|X{}v~}i|^{}w~}h|d{|s~}y|xy|}s~}[{|y}u~y}y|]{}w~}h|v~|iv~}r{|v~qv~}g{|t~}|uy|s~&{}p"
4020       "~} w{}w~ w{}o~| @y~}Q{}v~}|u{|}y~,{|w~}m{|w~}Vw~|T{|v~|s{|}~({|w~}|o{|}w~|P{}x~|  w{|w~}|o{|}w~|(x~}tw~ rw~K{}x"
4021       "~|Rw~ {{}o~}w{|x~} H{}x~|T{|w~r{}x~}-{}x~|hw~|d{}x~|hw~|_{}x~|mw~|%{|r~|R{|r~|R{|r~|R{|r~|R{|r~|R{}r~|Y{|v~|y{|"
4022       "v~}h|h{|s~}|t{|}u~}c{}w~}h|`{}w~}h|`{}w~}h|`{}w~}h|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}r~lv~d{|t~}|uy|s~a{|t~"
4023       "}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~-{|t~}|u{|}q~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}dv~}l{}v~`"
4024       "{}w~}M{|v~p{}w~|V{}x~}L{}x~}Q{|x~|ux~}Wx~|v{|w~} {{}q~| Aw~|Lw~|Qw~u{}x~| y{|x~}Lw~|Q{}x~tx~}#{|}r~}Rx~u{|}y~}|"
4025       "Q{}x~}L{}x~}Q{}x~|v{|x~}Wx~|v{}w~}  j{|w~L{}x~}Q{}x~|u{}x~ x{}x~}Uw~}  b{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|"
4026       "P{|w~|xx|av~|fv~|  j{|y~|#{}t~Sk~|c{|k~}y{|k~}_{|s~}  ?{}t~}y|     u{|u~|p{}y~}$v~}R{}x~}vw~}Sw~|tw~|[{|}m~}|h{"
4027       "|w~sw~|p{}x~|_{}v~|q{|}|Q{}x~}L{}w~Lw~}U{}y~|ux~u{|y~}U{|x}|  `w~|Z{|v~}s{|v~}]w~y}y|{}w~}X{}x~|p{|u~|^y}|n{|u~"
4028       "|U{}x~y}w~}\\{|w~}K{|u~}o{}|Mv~|_{}v~}q{|u~_{}v~}r{|v~|   jy~}|qu~|_{}t~}y|s{|}t~}\\{}w~}w~}Z{}w~}o{|u~}_{|t~|n"
4029       "{|}x~}g{}w~}n{|}t~}`{}w~}L{}w~}P{|t~}m{|}w~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}p{}u~|]{}w~}V{}w~}w~|l{}r~|h{}r~|mv~e{|"
4030       "u~}|p{|t~`{}w~}q{|}u~|c{|u~}|p{|t~b{}w~}p{}u~|_{|u~|n{|}y~W{|v~|Z{|v~h{}w~}g{|v~fv~|o{}w~}n{}x~}w~mv~|kv~}ov~}a"
4031       "{|v~|n{|v~|M{}v~}\\{|w~}N{|w~|E{|w~|U{}v~}{|u~| x{|x~}\"w~} `{|w~|$v~ w~} >w~}Dv~    Ov~   !v~Lw~|M{}w~|  <{|w~"
4032       "}p{|w~}Xn~|Zv~  _{|v~    !{|w~}p{}w~}X{}w~}w~}W{}v~|M{}w~}R{|t~|p{|t~|_{|}l~}|`{}w~}hv~|iv~}r{|v~qv~}h{|u~}|p{|"
4033       "t~({}n~} x{}w~ x{}m~| Ay~}R{|v~}p{}+{}w~|nv~Uw~|T{}w~| x{|w~|k{|w~|Q{|x~|  x{|w~|k{|w~|*{|x~rx~|R{|w}Fw~Kw~|S{}"
4034       "x~| {|n~}w{|x~} H{}x~|T{}x~}qw~|.{}x~|i{}x~}c{}x~|i{}x~}^{}x~|n{}x~}${}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w"
4035       "~}Rv~|w~}Y{}w~}x{|v~U{|t~|n{|}x~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}r~|mv~e{|u~}|p{|"
4036       "t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~/{|u~}|p{}t~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}d{|v"
4037       "~|n{|v~|`{}w~}M{}w~}ow~}U{}x~|N{|w~Px~}t{|x~|Xx|sy| w{}s~| @{|w~M{}x~|Q{}x~|tw~ x{}x~}N{}x~|Q{|x~|t{|x~|&{}t~}v"
4038       "~} t{}x~|N{|x~}Q{|x~}t{}x~|Xx|sy|  g{|x~}N{|x~}Q{|x~}sx~} {{|x~}Tw~}  d{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|R{|w~Z{}w~}"
4039       "g{}w~} Ay|J{}y~#{|s~}Tk~}c{}j~|{}j~_q~|  A{}u~}     q{}v~|n{}~}$v~}R{}x~}vw~}Sw~t{|w~\\{|h~|i{}x~}s{}x~}q{|x~}^"
4040       "v~|C{}x~}Lw~}L{}w~V{|v~|wx~w{|v~|V{}w~  a{|w~Yv~}q{|v~|^{}y|u{}w~}Xy}|m{|u~M{|v~}V{|w~|}w~}\\{|w~}Ku~|?{|v~^u~o"
4041       "{}v~|a{|v~}p{}v~   j{~|nv~}`u~}|l{|}u~]v~{v~Z{}w~}mu~_u~}j{|y~}g{}w~}l{|}u~}a{}w~}L{}w~}Q{|u~}i{|}y~|g{}w~}hv~|"
4042       "Y{}w~}M{}w~}W{}w~}q{}u~|\\{}w~}V{}w~|w~}lw~|v~|h{}q~mv~f{|u~}m{|u~}a{}w~}o{}v~}d{|u~}m{|u~}c{}w~}o{|u~_{}v~|j{|"
4043       "W{|v~|Z{|v~h{}w~}fv~|h{}v~n{}w~}nw~|w~|o{|v~j{|v~}q{}v~_{}v~nv~}M{|u~[{|w~}Mw~}E{|w~|V{}v~}x{|u~|  vw~} `{|w~|$"
4044       "w~} w~} >w~}Dv~    Ov~   !v~Lw~|M{}w~|  <{}w~|ow~}Xm~|[v~  ^v~|    \"v~|p{|v~Xv~{v~V{}v~|N{}w~}Ru~}l{}u~|b{|g~}"
4045       "|b{}w~}hv~|iv~}r{|v~qv~}i{|u~}m{|u~}*{}l~} y{}w~ y{}k~| By~}R{}v~ y{|w~}o{|w~}Uw~|T{}w~ x{|x~}g{}x~|R{|x~}  y{|"
4046       "x~}g{}x~|+{}y~}r{}y~}R{}w~Fx~}M{|}w~ Mm~}w{|x~} H{}x~|Tw~p{}x~|.{}x~|j{|w~b{}x~|j{|w~]w~n{|w~#v~{v~Rv~{v~Rv~{v~"
4047       "Rv~{v~Rv~{v~S{|w~}{}w~|Zv~|x{|v~Uu~}j{|y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}q~mv~f{|"
4048       "u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}1{|u~}m{|u~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w"
4049       "~}c{}v~nv~}_{}w~}Mv~n{}w~Tw}N{|x}P{|x}r{|x} F{|}x~}| ={|x}|O{|x}|Px}|s{|x}| xw|Nw|Pw|rw|'{|v~}|y{|v~} tw}Nw}P{|"
4050       "x}rx}|  6w|Nw|Ox|rw| Nw~}  e{}h~}\\{}h~}\\{}h~}\\{}h~}\\{}h~}S{|w~Z{|v~gv~| Ay~}L{|y~}${|q~}V{|j~ci~}|i~|a{}p~|"
4051       "Oy|Uw|jw|Vu|Wv|kw|b{}v~}     p{|v~|l{|}$v~}R{}x~}vw~}T{|x~}t{|x~}]{|g~|i{}x~|s{|w~qw~|^v~B{}x~}M{|w~|L{|w~}V{|}"
4052       "w~}xx~x{}w~}|U{}w~  a{}w~Z{|v~o{}w~}U{}w~}X{|j{}v~|M{}v~Vw~}{}w~}\\{|w~}L{|v~|>v~}_{|v~|nv~}a{}v~nv~|   \\{}w~}"
4053       "b{|u~|h{|}v~|`{|w~}{}w~|[{}w~}m{|v~|a{}v~}gy}g{}w~}j{}u~|b{}w~}L{}w~}Q{}v~}f{|~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}r{}"
4054       "u~|[{}w~}V{}w~y|w~m{|w~{v~|h{}w~}v~|nv~f{}v~}ju~|b{}w~}nu~d{}v~}ju~|d{}w~}n{}v~|`v~}D{|v~|Z{|v~h{}w~}f{}w~}hv~}"
4055       "n{|v~o{|w~{}x~}o{}w~}i{}v~|s{|v~|^v~}p{}v~M{|u~|[{|w~}M{}x~}E{|w~|W{}v~|v{|u~|  ww~} `{|w~|$w~} w~} >w~}Dv~    "
4056       "Ov~   !v~Lw~|M{|w~|  <{}w~|ow~}Xy~}w|}t~[v~|  _{}w~}    #{|w~}n{}w~|Z{|w~}{}w~|Vu~|O{}w~}S{}v~}j{}u~c{}d~|c{}w~"
4057       "}hv~|iv~}r{|v~qv~}i{}v~}ju~|,{}v~y}w~|v~} {{}w~ {{}v~y}w~|u~| Cy~}R{}w~}R{|ey|_{}w~|pv~Tw~|T{}w~ y{|x~}e{}x~|\\"
4058       "{|}p~}  {{|x~}e{}x~|,{}y~}r{}y~}R{}w~G{}x~|Rq~| N{|m~}w{|x~} H{}x~|U{|w~p{|x~}.{}x~|j{}x~|b{}x~|j{}x~|_{|w~|n{}"
4059       "x~|${|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{}w~|{|w~}[{|v~w{|v~V{}v~}gy}c{}w~}M{}w~}M{}w~}M{}w~"
4060       "}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~}v~|nv~f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|c{}d{}|d{}v~}"
4061       "k{}u~|f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}bv~}p{}v~^{}m~y}|Yv~o{|}w~         Py~}|u{|v~}       2w~}  f{"
4062       "}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~T{|w~Yv~|i{|v~ A{}x~}M{}y~|$o~|W{|j~ch~}i~}"
4063       "b{}n~T{|}t~y}|Zw~}kw~}X{}u~|X{}w~|m{}w~|d{|v~|     ov~}j{|$v~}R{}x~}vw~}T{}x~}t{}x~}]u~}|{|y~|y{|y}x~|iw~|rw~r{"
4064       "}x~}]v~B{}x~}Mv~Jv~T{|}w~|{x~{|w~}|S{}w~  aw~}Z{}w~}o{|v~U{}w~}Ev~}M{|v~W{}w~y{}w~}\\{|w~}Lv~}>{|v~|_{|v~m{}w~}"
4065       "av~|n{|v~ 8{|y}6{|~|4{}v~c{|v~}d{|v~`{}w~|{|w~}[{}w~}lv~|b{|v~}e{|g{}w~}i{}u~b{}w~}L{}w~}R{|v~}dy|g{}w~}hv~|Y{}"
4066       "w~}M{}w~}W{}w~}s{}u~Y{}w~}V{}w~|{w~|nw~}{v~|h{}w~y|v~nv~g{|v~}i{|u~b{}w~}n{|v~|f{|v~}i{|u~d{}w~}n{|v~|a{|v~C{|v"
4067       "~|Z{|v~h{}w~}f{|v~|j{|v~|mv~|p{|w~{|x~}ov~|hv~}sv~}]{|v~|r{|v~|Mu~|Z{|w~}M{|w~E{|w~|X{}v~|t{|u~|  xw~} `{|w~|$w"
4068       "~} w~} >w~}Dv~    Ov~   !w~}Lw~|M{|w~|  <v~nw~}X{|s{}v~}\\{}v~|  `{|v~    #{}w~|n{|w~}Z{}w~|{|w~}Uu~|P{}w~}T{|u"
4069       "~h{}v~}f{|r~y}v~}r~}d{}w~}hv~|iv~}r{|v~qv~}j{|v~}i{|u~-{}v~}{}w~{|v~} {}w~ {}v~}{}w~{|u~ Cy~}Rv~|S{}~}g{|y~|_v~"
4070       "q{}w~|Tw~|T{}w~| {{x~}t{|y}u~}|u{}x~^{}m~}  {{x~}wq}y|s{}x~,{}y~}r{}y~}R{}w~H{|x~}Qs~} L{}m~}w{|x~} H{}x~|U{|x~"
4071       "}p{|x~}.{}x~|k{|x~}a{}x~|k{|w~cx}u~|n{|x~}#{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}Tv~xv~[v~}w{|v"
4072       "~W{|v~}e{|c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~y|v~nv~g{|v~}i{|u~g{|v~}i{|u~g{|v~}i{"
4073       "|u~g{|v~}i{|u~g{|v~}i{|u~d{}y~f{}y~|f{|v~}k{|s~f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}b{|v~|r{|v~|^{}i~}|"
4074       "\\v~q{}t~|         F{}v~|    C{~|   mw~}  gu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|V{|w~Y{}w~}i{}w~} B{"
4075       "}w~}Mx~${}n~W{|k~}d{|U~}c{|m~}W{|n~}[w~}kw~}Xt~}X{|w~}mv~cv~|     o{|v~| mv~}R{}x~}vw~}Tw~|tw~|^{}v~|x{|y~|u{|~"
4076       "|iw~|rw~s{|w~\\v~B{}x~}N{|w~}J{}w~|S{|n~|Q{}w~  b{|w~|Zv~|nv~|V{}w~}E{}w~}M{|v~X{|w~|y{}w~}\\{|w~}M{|v~={}v~^{|"
4077       "v~m{}w~}b{|v~lv~| <{|}x~}6{|x~}|7{}w~}cv~|b{|w~}b{|v~xv~[{}w~}l{}w~}bu~|P{}w~}h{}v~}c{}w~}L{}w~}Ru~M{}w~}hv~|Y{"
4078       "}w~}M{}w~}W{}w~}t{}u~X{}w~}V{}w~|{}x~}o{|w~|{v~|h{}w~|{v~}ov~gu~|h{}v~|c{}w~}mv~}fu~|h{}v~|e{}w~}mv~}a{|v~C{|v~"
4079       "|Z{|v~h{}w~}ev~}j{}v~l{}w~}p{}x~}{|w~ov~|h{|v~}u{}v~[{}v~rv~}M{}v~}Y{|w~}Lw~|F{|w~|Y{}v~|qu~| Kt|Uw~}uu|Mt|Ru|u"
4080       "{|w~|Wt|Ow~}Mu|Tw~}uu| Jw~}Dv~Tu|mv|Vu|Pt|Ku|Qu|Bv|Us|Rv~   !w~}Lw~|M{|w~|  iv|Sv~o{|w~}N{}v~\\{|t~}|Is|Mu| u{}"
4081       "w~|   Zt| Lv~|n{|v~[{|v~xv~Tu~P{}w~}T{}v~|gu~g{|t~}|y{|v~x{}t~}e{}w~}hv~|iv~}r{|v~qv~}ju~|h{}v~|/{}v~}y{}w~y{|v"
4082       "~}!{}w~!{}v~}y{}w~y{|u~ F{|}y~}x|V{|v~S{}x~}i{|w~|`{}w~|rw~}Sw~|T{|v~|!{}y~}u{|n~}v{}y~}a{|k~}  {}y~}vn~}t{}y~}"
4083       "-{}y~}r{}y~}R{}w~I{|w~Pt~}| L{}m~}w{|x~} H{}x~|U{|x~}p{|w~.{}x~|kw~|a{}x~|kw~|ct~}lw~|${|v~xv~U{|v~xv~U{|v~xv~U"
4084       "{|v~xv~U{|v~xv~U{|w~}x{}w~|]{|v~v{|v~Wu~|L{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{|v~}f{}w~|{v~}o"
4085       "v~gu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|f{}w~h{}w~|gu~|l{|r~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~"
4086       "h{}w~}a{}v~rv~}]{}g~}]w~}s{|r~|Xt|Nt|Nt|Nt|Nt|Nt|Xt|lu|Ut|Pt|Nt|Nt|Nt|  0{}v~|Pu|Pt|Nt|Nt|Nt|Nt| ut|t{}y~}   nw"
4087       "~}uu|  t{}w~}|wv|v{}v~b{}w~}|m{}v~b{}w~}|m{}v~b{}w~}|m{}v~b{}w~}|m{}v~V{|w~Xv~iv~| C{|v~M{|y~}%{}m~}Wk~}d{|U~}d"
4088       "{|k~}Y{}k~|]w~}kw~}Y{|s~X{|v~n{|w~}d{}w~}     n{}w~} lv~}R{}x~}vw~}U{|w~t{|w~]v~|w{|y~|`w~|rw~s{}x~|\\v~|C{}x~}"
4089       "N{}w~|J{|w~}Q{|r~|O{}w~  b{}w~Z{|v~m{}w~}V{}w~}E{}w~}M{|v~Xw~}x{}w~}\\{|w~}M{}w~}=v~}^{|v~m{}w~}b{|v~lv~} ?{|}u"
4090       "~}6{|u~}|:{}w~}d{}w~|`{|w~}c{}w~|x{}w~}\\{}w~}l{}w~}c{|v~}O{}w~}gu~c{}w~}L{}w~}S{|v~}M{}w~}hv~|Y{}w~}M{}w~}W{}w"
4091       "~}uu~}W{}w~}V{}w~|{|w~|p{}w~yv~|h{}w~|{|v~ov~h{|v~}fu~c{}w~}mv~}g{|v~}fu~e{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}e{}v~j"
4092       "v~|l{}w~}pw~|yw~|q{|v~f{}v~|w{|v~|Zv~}t{}v~M{}v~}X{|w~}L{}x~}F{|w~|Z{}v~|o{}v~| P{|}q~}|Xw~}w{}s~}|S{|}q~}|X{}s"
4093       "~}|x{|w~|Z{|}r~}|W{}k~}W{}s~}|x{|w~|`w~}w{|s~}|Rv~Lv~Tw~}n{|v~}Xv~_w~}w{}s~}r{|s~}cw~}w{|s~}|V{|}r~}|Yw~}w{}s~}"
4094       "|V{}s~}|x{|w~|Zw~}w{}t~|Y{}o~}|Z{}i~]{|w~|m{}w~|c{|v~iv~i{}w~|pu~ow~}hv~}m{|v~|d{|v~iv~`d~Uw~}Lw~|M{|w~|  l{|s~"
4095       "}|u{}x~}av~o{|w~}M{}w~|\\{}q~}|P{}o~}|\\w~}w{|s~}|^x~y}hv~W{}w~}X{|w~|m{}w~|d{}w~}h{}w~}]{|y}w{|}x~}|]_~|dv~t{}"
4096       "w~t{|w~}[{|q~}|U{|y}i~}f{|`~b{|v~lv~|\\{}w~|x{}w~}U{|u~Q{}w~}U{|v~}f{|v~|ht~|w{|v~v{}u~}f{}w~}hv~|iv~}r{|v~qv~}"
4097       "k{|v~}fu~/{|w~}x{}w~x{|w~}I{|T{}w~S{|i{|\\w~}x{}w~x{|w~|!v~}O{|}p~}|Y{|v~T{|v~}k{|v~}_v~s{}w~|Sw~|Su~|#{|x~u{}l"
4098       "~ux~|bv~}y|v{|x~} !{|x~ul~|ux~|.{|x~|t{|x~|R{}w~J{|w~|L{|}x~}&{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|w~.{}x~|l{"
4099       "}x~}`{}x~|l{}x~}br~|o{}x~}Qv~|S{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{|w~}]{}w~}v{|"
4100       "v~X{|v~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|{|v~ov~h{|v~}fu~i{|v~}fu~i{|v~}fu~i{|v~}"
4101       "fu~i{|v~}fu~g{|u~j{}v~}h{|v~}l{|w~}v~}g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`v~}t{}v~\\{}f~}^w~}t{}v~}y|Y"
4102       "{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|_{|}q~}|r{|}r~}[{|}q~}|W{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|Qv~Lv~Lv~"
4103       "Lv~O{|y}w~}u~|\\w~}w{|s~}|V{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|Q{}u~Q{|}r~}|x{}x~}b{|w~|m{}w~|a{|w~|m{}w~|a{"
4104       "|w~|m{}w~|a{|w~|m{}w~|c{|v~iv~aw~}w{}s~}|^{|v~iv~ W{}w~}u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{"
4105       "}w~}W{}w~X{}w~}k{|v~ C{|v~|M{}y~|&{|k~}X{}l~|cU~}di~|[{}i~|^w~}kw~}Y{}s~|Xv~|o{}w~|dw~}     mv~| lv~}R{}x~}vw~}"
4106       "^{}Z~f{|w~}v{|y~|`w~|rw~t{|x~}[{}w~}C{}x~}Nv~Hv~O{}v~}M{}w~  bw~}Z{}w~}m{|v~V{}w~}E{}w~}M{|v~Y{}w~w{}w~}\\{|w~}"
4107       "Mv~|>{|v~]{|v~m{}w~}b{|w~}l{}w~}W{|v}M{}v~D{}r~}6{|r~}|>{|v~|e{}w~|^{|w~|dv~w{|v~\\{}w~}lv~|c{}v~N{}w~}g{}v~|d{"
4108       "}w~}L{}w~}S{}v~L{}w~}hv~|Y{}w~}M{}w~}W{}w~}vu~}V{}w~}V{}w~|yw~}pw~}yv~|h{}w~|y{}w~}pv~h{}v~e{}v~|d{}w~}mv~}g{}v"
4109       "~e{}v~|f{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}dv~|l{|v~k{|v~q{|w~x{}x~}q{}w~}e{}v~wv~}Y{|v~|v{|v~|N{|v~}W{|w~}L{|w~F{|"
4110       "w~|[{}v~l{}v~ S{|}k~|Zw~}y{|o~}V{|k~|\\{|o~}y{|w~|\\{|m~}X{}k~}Y{|o~}y{|w~|`w~}y{|o~}Sv~Lv~Tw~}o{|v~}Wv~_w~}y{|"
4111       "o~|v{|o~|ew~}y{|o~}Y{|}n~}|[w~}y{|o~}Y{|o~}y{|w~|Zw~}y{|r~|[{}j~[{}i~]{|w~|m{}w~|b{}w~|k{|w~}i{|w~}q{|u~|q{|w~|"
4112       "h{|v~|o{|v~}b{}w~|k{|w~}`d~Uw~}Lw~|M{|w~|  n{|o~}vw~|av~o{}w~|M{|v~[{|o~}|U{}k~}]w~}y{|o~}_u~|k{|w~}Wu~X{|w~|m{"
4113       "}w~|dv~|h{|v~_{}x~}x{}s~}__~|dv~t{}w~t{|w~}\\{}n~}Y{|}e~}f{|`~b{|w~}l{}w~|\\v~w{|v~T{|u~R{}w~}U{}v~dv~}i{}u~u{|"
4114       "v~u{|u~|g{}w~}hv~|iv~}r{|v~qv~|k{}v~e{}v~|c{~}I{|y~}w{}w~w{|y~}I{}~|U{}w~T{}~|k{}~|\\y~}w{}w~w{|y~| v~}P{}k~Z{|"
4115       "v~S{|v~}x{|}v~}|y{|v~}^{|w~}u{|w~}Rw~|S{|u~}${}y~|v{}v~}|wy|}y~u{|y~}c{|x~}r{|x~}Q{|q{| W{}y~|uw~vy|v~u{|y~}-w~"
4116       "|v{|w~Q{}w~K{|w~|I{|w~'{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|x~}]{|q{|X{}x~|m{|w~_{}x~|m{|w~]{|}w~}q{|w~Pv~|Sv"
4117       "~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~W{|v~vv~^{|v~|v{|v~X{}v~J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z"
4118       "{|v~g{|v~}g{}w~|y{}w~}pv~h{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|g{|u~l{}v~}g{}v~kw~}{}v~g{|v~h{"
4119       "}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`{|v~|v{|v~|\\{}w~}s|y}t~}_w~}u{|v~|Y{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|"
4120       "}k~|d{|}k~|v{|m~}_{|k~|[{|m~}W{|m~}W{|m~}W{|m~}Rv~Lv~Lv~Lv~Q{|}l~\\w~}y{|o~}Y{|}n~}|X{|}n~}|X{|}n~}|X{|}n~}|X{|"
4121       "}n~}|S{}u~S{|}n~}{|x~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{}w~|k{|w~}aw~}y{|o~}^{}w~|k{|w~} X{|w~}"
4122       "t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}ly|y{|w~}f{|w~}h{|w~}X{}x~}X{|v~kv~| Cv~|Lx~&{|i~|Y{|m~}bU~|e{}"
4123       "h~\\{|u~}|xy|}u~^w~}kw~}Yr~}X{}w~}ov~d{}w~     lv~| lv~}R{}x~}vw~}^{}Z~f{|w~|v{|y~|`w~|s{|w~tw~|[{|v~|D{}x~}Nw~"
4124       "}H{}w~|Q{|t~|N{}w~  c{|w~|Zv~|lv~|W{}w~}E{}w~}M{}w~}Z{|w~|w{}w~}\\{|w~}N{|v~={}w~}\\v~|nv~|b{}w~}l{}v~W{}v~M{}v"
4125       "~G{|}p~|6{|o~}@u~e{|w~|\\{}w~e{|w~}v{}w~|]{}w~}m{|v~|cv~}N{}w~}g{|v~}d{}w~}L{}w~}Sv~}L{}w~}hv~|Y{}w~}M{}w~}W{}w"
4126       "~}x{|u~}U{}w~}V{}w~|y{}w~q{|w~|yv~|h{}w~|y{|v~pv~hv~}e{|v~}d{}w~}mv~}gv~}e{|v~}f{}w~}mv~}a{|v~|D{|v~|Z{|v~h{}w~"
4127       "}d{}w~}l{}w~}jv~|r{|w~x{|x~}qv~|e{|v~}y{}v~W{}v~vv~}N{|u~V{|w~}Kw~|G{|w~|\\{}w~}j{}v~ T{}i~}[w~}{}m~}X{}j~|]{}m"
4128       "~}{|w~|]{}j~Y{}k~}Z{}m~}{|w~|`w~}{|l~Tv~Lv~Tw~}p{}v~}Vv~_w~}{|m~|x{|m~|fw~}{|m~}[{|j~|\\w~}{}m~}[{}m~}{|w~|Zw~}"
4129       "{|q~|\\{}i~[{}i~]{|w~|m{}w~|b{|w~}k{}w~|hw~}q{|u~}q{}w~|g{}v~ov~}a{|w~}k{}w~|`d~Uw~}Lw~|M{|w~| Gy|l{|Z{}m~}x{|w"
4130       "~`v~p{|v~Kv~Z{|m~|X{}j~}]w~}{|l~`t~|l{}w~|X{|u~}Y{|w~|m{}w~|e{}v~f{}w~}b{|v~}y{|q~}`_~|dv~t{}w~t{|w~}^{|k~}[{|c"
4131       "~}f{|`~b{}w~}l{}w~}]{|w~}vv~|T{|v~}S{}w~}Uv~}d{}v~j{|u~t{|v~t{|u~g{}w~}hv~|iv~}r{|v~r{|v~|kv~}e{|v~}dx~}I{|}v{}"
4132       "w~v{|}I{}x~|V{}w~U{}x~|m{}x~|\\{|v{}w~vy| {{v~}R{|i~Z{|v~R{|v~}|q~}|v~}\\v~u{}w~Qw~|R{|t~|'{|y~}v{}w~}p{|t{}y~|"
4133       "d{}x~|r{|x~}Ry}r{|~ X{|y~}tw~sw~|u{}y~|.{|w~}x|}w~|Q{}w~L{|w~|G{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|w~p{|x~}]"
4134       "{~|r{|}Y{}x~|mw~|_{}x~|m{}x~|[{|w~|r{}x~|Pv~|T{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{}w~}"
4135       "v{}w~}_{}w~}u{|v~Xv~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fu~g{}w~|y{|v~pv~hv~}e{|v~}jv~}e{|v~}"
4136       "jv~}e{|v~}jv~}e{|v~}jv~}e{|v~}f{|u~n{}v~}fv~}l{}x~}y{|v~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}_{}v~vv~}["
4137       "{}w~}q{|}u~|`w~}uv~W{}i~}[{}i~}[{}i~}[{}i~}[{}i~}[{}i~}e{}i~}x{}k~}a{}j~|\\{}j~Y{}j~Y{}j~Y{}j~Sv~Lv~Lv~Lv~R{}j~"
4138       "}]w~}{|m~}[{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|T{}u~T{|f~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{|w~}k{}w~|a"
4139       "w~}{}m~}_{|w~}k{}w~| Xw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}l{|y~}y{}w~fw~}f{}w~X{}x~}Wv~|m{|v~ C{}w~}"
4140       "[{|}|o{|y~|&g~|Y{}n~|b{}V~e{|g~}]v~}r{|v~}_w~}kw~}Z{|r~}X{|v~p{|w~}dw~}     pw|v~l| {{v~}R{}x~}vw~}^{}Z~f{|w~|v"
4141       "{|y~|`{}x~}s{|x~}u{}x~}Y{}v~|E{}x~}O{|w~}H{}w~|S{|}r~}|P{}w~  c{|w~Yv~|lv~|W{}w~}Ev~|N{|v~|Zw~}v{}w~}\\{|w~}|}v"
4142       "~y}|X{}w~}>{|v~|\\{}w~}o{|v~a{}w~}l{}v~W{}v~M{}v~J{|}p~}|2{|}p~}|D{}v~|e{}x~}p{|}w~}|vx|uw~|f{}w~|v{|w~}]{}w~}m"
4143       "{}v~c{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|u~}T{}w~}V{}w~|y{|w~|r{}x~}xv~|h{}w~|x{}w~"
4144       "}qv~i{|v~|dv~}d{}w~}mv~}h{|v~|dv~}f{}w~}n{|v~|`u~D{|v~|Z{|v~h{}w~}d{|v~m{|v~|j{}w~}r{}x~}x{|w~qv~|d{}v~y|v~|Vv~"
4145       "}x{}v~Mu~|V{|w~}K{}x~}G{|w~|]{}w~}h{|v~ U{}u~v}s~}\\w~}|v~w}t~}Zr~v}v~|^{}t~w}v~}|w~|^{}t~v}t~Zv}v~s}[{}t~w}v~}"
4146       "|w~|`w~}|u~x}t~}Uv~Lv~Tw~}q{}v~|Uv~_w~}|v~x}s~y{|v~x}s~fw~}|u~x}t~}]{|s~x}s~|]w~}|v~w}t~}]{|t~w}v~}|w~|Zw~}|t~}"
4147       "x~|]{}t~u}u~[{|x}v~q}]{|w~|m{}w~|av~kv~g{}w~q{}t~qv~e{}v~q{}v~_v~|m{|v~_d~Uw~}Lw~|M{|w~| J{|}v~}r{}v~}|_{}u~w}u"
4148       "~|y{}x~}`v~q{|v~}K{}w~|\\{}w~}p~}Z{}s~w}u~}]w~}|u~x}t~}as~m{|v~W{}t~Y{|w~|m{}w~|ev~|f{|v~c{|u~}yn~a_~|dv~t{}w~t"
4149       "{|w~}_{|t~w}t~}]{|b~}f{|`~b{}w~|l{}w~}]{}w~|v{|w~}S{|v~}T{}w~}Uv~|d{|v~|k{}v~|t{|v~s{}v~|h{}w~}hv~|i{}w~}r{|v~r"
4150       "{|v~|l{|v~|dv~}ev~}C{}w~C{}v~|W{}w~V{}v~n{|v~|W{}w~ sv~}S{|s~}y~x}v~Z{|v~Q{|e~}[{|w~}w{|w~}Qw~|R{}r~|){}y~|w{|w"
4151       "~}g{|y~}dw~q{}x~}S{}~}s{}y~ X{}y~|tw~s{}x~}u{|y~}-{}p~}P{}w~M{|w~|F{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|Tw~p{}x~"
4152       "|]y~}s{|y~Z{}x~|n{|x~}^{}x~|n{|w~Y{|x~}s{|x~}Ov~|T{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}Xv"
4153       "~u{|v~_v~|u{|v~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|x{}w~}qv~i{|v~|dv~}k{|v~|d"
4154       "v~}k{|v~|dv~}k{|v~|dv~}k{|v~|dv~}e{|u~p{}v~}f{|v~|m{}w~wv~}h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^v~}x{}v"
4155       "~Z{}w~}o{}v~}`w~}v{|w~|W{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}f{}u~v}s~}{s~w}t~}cr~v}"
4156       "v~|]{}t~v}t~[{}t~v}t~[{}t~v}t~[{}t~v}t~Tv~Lv~Lv~Lv~S{}h~|^w~}|u~x}t~}]{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~"
4157       "|\\{|s~x}s~|U{}u~U{|s~x}q~|`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|av~|m{|v~`w~}|v~w}t~}_v~|m{|v~ X{|w~"
4158       "r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~l{|w~}yw~}h{|w~dw~}Y{}x~}W{}w~}m{}w~} Xg|}v~s|e{|}x~}o{}y~&{}f~Y{|o"
4159       "~}a{|V~f{|e~}_{|w~}p{|v~_w~}kw~}Z{}w~}v~Wv~|q{}w~}e{|w~     pc~} {{v~}R{|x}|v{|x}|^{}Z~f{|w~|v{|y~|`{|w~s{}x~}v"
4160       "{|w~Wu~|F{|x}|O{}w~|H{|w~}U{|}w~|x~|w~}|R{}w~  c{}x~}Yv~|lv~|W{}w~}F{|v~N{|v~}Z{}w~u{}w~}\\{|k~}Z{}w~}x{|}u~y}|"
4161       "L{}v~Zv~|pv~}a{|v~l{}v~|X{}v~M{}v~M{|}p~}|,{|}p~}|H{}v~|e{|w~q{|q~}y{}x~|v{|x~}fv~tv~]{}w~}n{}v~|c{|v~|N{}w~}f{"
4162       "}v~d{}w~}L{}w~}T{}v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}{|u~}S{}w~}V{}w~|xw~}rw~|xv~|h{}w~|x{|v~|rv~i{|v~|d{}v~d{}w~}n"
4163       "{|v~|h{|v~|d{}v~f{}w~}n{}v~|`{}v~}|F{|v~|Z{|v~h{}w~}cv~|n{}v~i{}w~}rw~|ww~|s{|v~b{}q~}U{|v~|{|v~|N{}v~|U{|w~}K{"
4164       "|w~G{|w~|^{}w~}f{|v~ V{}y~}|r{|u~|]r~|u{|u~}\\{}u~}s{|}y~|_{|u~|u{|}s~|_{}v~}|t{}v~}Vw~}T{|u~|u{|}s~|`r~|u{|u~|"
4165       "Vv~Lv~Tw~}ru~|Tv~_r~|v{|}v~}{w~|u{}v~}gr~|u{|u~|^u~}|v{|}u~]r~|u{|u~|_{|u~|u{|}s~|Zr~}|v{|\\v~}|r{|}y~Wv~S{|w~|"
4166       "m{}w~|a{}w~|m{|w~}g{}w~|rs~qw~}dv~}s{|v~|_{}w~}m{}w~|Nu~Uw~}Lw~|M{|w~| K{}r~u{|r~}a{|v~}|v{}v~yw~|`v~r{|u~|K{|w"
4167       "~|]{}w~|xy|}t~}[u~}|s{|}~}]r~|u{|u~|ay|v~|n{}w~|X{|s~|Z{|w~|m{}w~|f{|v~dv~|e{|u~}|{|v~y|}v~}bx}u~q}u~x}|dv~t{}w"
4168       "~t{|w~}_u~|u{|u~|_{|u~}|v{|}t~v}f{|q}u~p}b{}w~|l{|v~]v~tv~R{}v~}U{}w~}V{|v~|cv~}l{|v~}s{|v~s{|v~}h{}w~}hv~|i{}v"
4169       "~r{|v~r{|v~|l{|v~|d{}v~fu~|C{}w~C{|u~|X{}w~W{}v~}m{}v~|X{}w~ sv~}T{|u~}|yy~}x{|}y~Z{|v~P{|g~}Y{}w~|xv~Pw~|T{|v~"
4170       "}u~}*x~v{}w~ex~dw~qw~}U{|x~}t{}x~ Xx~sw~s{}x~}tx~,{|r~|O{}w~N{|w~|Dw~({|w~|m{}w~|a{|m~}w{|x~} H{}x~|T{}x~}qw~|]"
4171       "x~}t{|x~|\\{}x~|nw~]{}x~|nw~|Xw~sw~|Ov~|Tv~tv~Xv~tv~Xv~tv~Xv~tv~Xv~tv~Y{|w~}tv~|a{|v~t{|v~Y{|v~|J{}w~}M{}w~}M{}"
4172       "w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|x{|v~|rv~i{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{"
4173       "}v~d{|u~r{}v~}e{|v~|n{}w~v{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^{|v~|{|v~|Z{}w~}nu~`w~}v{}w~V{}y~}|r"
4174       "{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|g{}y~}|r{|o~}|u{|}v~}e{}u~}s{|}y~|^{}v~}|"
4175       "t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}Uv~Lv~Lv~Lv~T{}u~}|v{|}v~}^r~|u{|u~|^u~}|v{|}u~\\u~}|v{|}u~\\u~}|v"
4176       "{|}u~\\u~}|v{|}u~\\u~}|v{|}u~U{}u~Uu~}|u{}u~|_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{}w~}m{}w~|`r~|u{"
4177       "|u~|`{}w~}m{}w~| Xw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|m{|u~y{|w~hw~|d{|w~Y{}x~}Vv~mv~| XZ~}g{}t~oy~}'{}"
4178       "e~}Y{}p~_W~|fc~|`v~n{}w~|`w~}kw~}Zv~|}w~|X{}w~}qv~|e{}x~}     q{|c~| {{v~} y{|x~}t{}x~}]{|w~}v{|y~|_w~|u{|w~|vw"
4179       "~|Wt~ p{}w~|H{|v~V{}w~}yx~y{}w~}S{}w~  cw~|Z{|v~k{}w~}W{}w~}Fv~}Qy|u~}Z{|w~|u{}w~}\\{|i~|\\v~|y{}p~}|Nv~}Z{|v~|"
4180       "s{|v~}`{|v~lu~|X{}v~M{}v~P{|}p~}|b{|Z~}b{|}p~}|L{}v~}d{}x~|r{|n~{}x~|uw~|h{}w~}t{}w~|^{}w~}q{|}u~}b{}v~M{}w~}f{"
4181       "}v~d{}w~}L{}w~}T{}v~K{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|x{|w~s{}w~wv~|h{}w~|w{}w~}rv~i{}v~c{}v~d{}w~}n{"
4182       "}v~|h{}v~c{}v~f{}w~}o{|u~_{|t~}|H{|v~|Z{|v~h{}w~}c{}v~nv~}i{|v~s{|w~|w{}x~}s{}w~}b{|q~S{}v~|v~}N{}v~}T{|w~}K{|w"
4183       "~|H{|w~|  s{}|m{}w~}]t~}q{}v~|^{}v~}ny|_u~q{}t~|`{|v~|q{|v~|Ww~}Tu~q{|t~|`t~}r{|v~}Vv~Lv~Tw~}t{|u~Rv~_t~}r{}v~}"
4184       "y~}r{}v~gt~}r{|v~}_{}v~|r{|v~}^s~q{}v~_{}v~|r{}t~|Zs~T{|w~}m{|Wv~S{|w~|m{}w~|a{|w~}mv~|g{|w~}s{|s~|s{|w~|d{|v~|"
4185       "u{|v~}]v~mv~N{}v~Tw~}Lw~|M{|w~| L{}p~w{|p~}bv~}s{}w~y|w~_v~wx|}t~}J{|w~}^{}w~r{}u~|]{|v~|Ot~}r{|v~}_{|v~nv~W{}s"
4186       "~}Z{|w~|m{}w~|f{}w~}d{}w~}eu~}x{|w~|x{}v~|`{|w~}q{|w~}`v~t{}w~t{|w~}`{}v~q{}v~_u~}r{|v~}V{|w~}Wv~|l{|v~^{}w~}t{"
4187       "}w~|R{}v~}V{}w~}V{|v~bv~}l{|v~|s{|v~r{}v~h{}w~}hv~|i{}v~r{|v~r{}v~k{}v~c{}v~gu~|B{}w~B{|u~|Y{}w~X{}v~}k{}v~|Y{}"
4188       "w~ sv~}Tu~|wy~}u{|Z{|v~O{|u~}|x{|}v~}_{|p~}y{|p~}Ww~|Tw~}y{|t~|,y~}vw~|e{}y~dw~|s{}w~}V{|w~}u{}w~ Xy~}sw~s{}x~}"
4189       "t{}y~*y}x~}|[m|}w~l|^{}w~C{|x~}({|w~|m{}w~|`m~}w{|x~} H{}x~|T{|w~|s{}x~}\\w~}u{|w~|]{}x~|o{}x~}]{}x~|o{}x~}Ww~t"
4190       "{}x~}Nv~|U{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~|t{|w~}av~}t{|v~Y{}v~I{}w~}M{}w~}M{}w"
4191       "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|w{}w~}rv~i{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~c{|"
4192       "u~t{}v~}d{}v~n{|w~|v{|v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}]{}v~|v~}Y{}w~}n{|v~|aw~}vv~V{}|m{}w~}]{}|m"
4193       "{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}g{}|m{}r~|q{|v~|g{}v~}ny|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~"
4194       "|q{|v~|Vv~Lv~Lv~Lv~U{|v~}q{|v~|_t~}r{|v~}_{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}V{}u~V{}v~"
4195       "|r{|v~}_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`v~mv~_s~q{}v~_v~mv~ X{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~"
4196       "|x{|u~|x{}x~|j{|w~m{|u~|x{}x~|j{|w~b{}x~|Z{}x~}V{}w~|o{|v~ WZ~}gx~}w~|q{}y~|({|c~}_v|{}r~u|d{}X~f{}b~|b{|w~}mw~"
4197       "}`w~}kw~}[{|v~{}w~}X{|w~}r{|v~d{}x~|     q{}c~ yv~} y{}x~}t{}x~}\\v~}w{|y~|_{}w~|vw~}v{|x~}X{|r~ qv~Fv~X{}w~}|x"
4198       "x~x{|}w~}U{}w~  d{|w~Y{|v~k{}w~}W{}w~}G{}v~|Xm~}Y{}x~}t{}w~}\\{|h~}]v~y|l~}P{|v~|Y{|u~u|}v~}_{|v~|n{|u~|X{}v~M{"
4199       "}v~R{|o~}|`{|Z~}_{|}p~}|P{}v~}cw~r{|l~}x~|u{|x~|hv~|t{|v~^{}e~}a{}v~M{}w~}f{|v~|e{}d~|_{}g~|d{}v~K{}^~|Y{}w~}M{"
4200       "}w~}W{}p~|Q{}w~}V{}w~|ww~|tw~}wv~|h{}w~|vv~|sv~i{}v~c{|v~|e{}w~}o{|u~g{}v~c{|v~|g{}w~}p{|u~|^{}q~y}|M{|v~|Z{|v~"
4201       "h{}w~}c{|v~|p{|v~gv~|t{|w~v{|x~}sv~|a{|s~|Rq~}N{}v~}S{|w~}Jw~}H{|w~|  bv~|^t~ov~}^v~}P{|v~|p{}u~|`v~|o{|v~Ww~}U"
4202       "{|v~o{}u~|`u~}p{|v~Vv~Lv~Tw~}u{|v~}Qv~_u~}pt~}pv~|hu~}p{|v~`{|v~|p{|v~|_t~ov~}a{|v~|p{}u~|Zt~S{}w~Gv~S{|w~|m{}w"
4203       "~|`v~|o{|v~ev~s{|x~y}x~}s{}w~|c{}v~uv~}\\{}w~|o{|w~}O{}v~|U{|w~}Lw~|M{|w~} M{|x~}x|}w~}xv~}x|}x~|d{}v~qw~y}x~}_"
4204       "v~x{}q~}I{|w~}_{|w~|q{|u~]{}w~|Nu~}p{|v~^{}w~|p{|w~}X{|q~Z{|w~|m{}w~|fv~|d{|v~f{|v~}w{}w~|wu~`{|w~}q{|w~}`v~t{}"
4205       "w~t{|w~}a{|v~ov~}a{|v~}p{}v~|W{|w~}Wv~}l|}v~^v~|t{|v~Q{}v~}W{}w~}V{|v~b{}w~}l{}v~r{|v~r{}v~|i{}w~}hv~|i{|v~|s{|"
4206       "v~r{}v~k{}v~xi~}y{|v~|iu~|A{}w~A{|u~|Z{}w~Y{}v~}i{}v~|Z{}w~ sv}|U{}v~|vy~}S{|v~O{|w~}s{|v~_{|o~|{o~}Ww~|U{}x~}v"
4207       "{}u~}.{|y~|w{|w~d{|y~|e{}w~t{}v~}W{|v~|v{}w~}cY|8{|y~|sw~sw~|t{|y~| `{|Z~}_{}x~}C{|w~}({|w~|m{}w~|`{|n~}w{|x~} "
4208       "H{}x~|Sv~|u{}w~|\\{}v~v{|v~|^{}x~|p{|w~\\{}x~|p{|w~W{|x~}u{|w~Mv}|Uv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~"
4209       "Zv~rv~b{|v~s{|c~l{}v~I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|vv~|sv~i{}v~c{|v~|l{}v~c{|v"
4210       "~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|c{|u~v{}v~}c{}v~o{|w~|u{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\q~"
4211       "}X{}w~}mv~}aw~}vv~Ev~|Mv~|Mv~|Mv~|Mv~|Mv~|Ws~|o{}w~}gv~}Ov~|o{|v~_v~|o{|v~_v~|o{|v~_v~|o{|v~Vv~Lv~Lv~Lv~Uv~}o{}"
4212       "w~}_u~}p{|v~`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|Wt|W{|v~|q{}u~|`{|w~|m{}w~|a{|w~|m{}w~|"
4213       "a{|w~|m{}w~|a{|w~|m{}w~|`{}w~|o{|w~}_t~ov~}`{}w~|o{|w~} X{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|yu~|w{|x~}j{}x~}"
4214       "mu~|w{|x~}j{}x~}b{|x~}Z{}x~}V{|v~o{}w~} WZ~}g{}|yw~}qx~'a~|c{|}t~}k~}|fY~}g{}`~b{|w~|m{}w~`w~}kw~}[{|w~}{|v~Wv~"
4215       "r{}w~}dw~|     lv~| kv~| yw~|tw~|\\{}v~}|y{|y~|^v~}y|}v~uw~X{|p~ rv~Fv~Xw~|vx~v{|w~U{}w~  d{}x~}Y{|v~k{}w~}W{}w"
4216       "~}H{|v~}Wo~}|Y{|w~|t{}w~}\\{|v~x}|x}s~}^v~|j~}Q{}w~}V{}l~}]v~}n{}u~}X{}v~M{|v}U{|}p~}|]{|Z~}\\{}o~|S{}v~}c{|x~}"
4217       "rv~}|w{|}t~|tx~}i{|v~rv~|_{}h~}|_v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~|P{}w~}V{}w~|w{}w~u{|w~|"
4218       "wv~|h{}w~|v{}w~}sv~iv~}c{|v~|e{}w~}p{|u~|gv~}c{|v~|g{}w~}sy|}u~}\\{}m~}|Q{|v~|Z{|v~h{}w~}bv~}p{}w~}g{}w~}t{}x~}"
4219       "v{|w~sv~|`{}u~}Q{|r~|O{|u~R{|w~}J{}w~H{|w~|  b{|w~}^u~|o{|v~_{}v~Ov~}nu~|a{}w~}m{}w~|Xw~}Uv~|nu~|`u~nv~|Wv~Lv~T"
4220       "w~}v{}v~}Pv~_u~o{}u~|p{}w~}hu~nv~|a{}w~}n{}w~}_u~|o{|v~a{}w~}nu~|Zu~|S{}w~Gv~S{|w~|m{}w~|`{}w~}o{}w~}e{}w~s{}x~"
4221       "}|w~sv~a{}v~w{}v~[{|w~}ov~|P{}v~|T{|w~}Lw~|M{|w~}:{|4x~|v{|w~}{}x~}u{}x~dv~}q{}s~|_v~x{}r~}S{|y}~y}|w{|w~}_w~}o"
4222       "{|v~}^{}w~Mu~nv~|_{|w~}pv~|X{}w~}v~|[{|w~|m{}w~|g{|v~bv~|g{}v~v{}w~v{|v~|a{|w~}q{|w~}`v~t{}w~t{|w~}a{}w~|o{|v~a"
4223       "{}v~nv~}W{|w~}W`~_{|v~rv~|Q{}v~|X{}w~}V{|v~b{}w~}lu~r{|v~r{|v~|i{}w~}hv~|hv~}s{|v~rv~}kv~}xi~}y{|v~|ju~|@{}w~@{"
4224       "|u~|[{}w~Z{}v~}g{}v~|[{}w~ Gv~}uy~}S{|v~Ow~}q{|w~|`{|n~}o~}Ww~|Uw~|t{}u~|0{|y~|w{|x~}d{|y~|e{|v~}w|t~}X{|v~|vv~"
4225       "}c{|Z~}8{|y~|sw~t{}w~s{|y~| `{|Z~}`{}x~}M{|~}|v{|}v~'{|w~|m{}w~|_{}o~}w{|x~}Vv}| s{}x~|S{|v~}|{y|}w~}Z{}v~|w{|v"
4226       "~}_{}x~|pw~|o{}w~m{}x~|p{}x~|vy|}w~y}|g{|w~|u{}x~|o{}w~3{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{}w~}"
4227       "r{}w~|c{}w~}s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|v{}w~}sv~iv~}c{|v~|lv~}c{|"
4228       "v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|b{|u~x{}v~}bv~}p{|w~}t{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\{|r~|"
4229       "X{}w~}mv~}aw~}v{}w~}F{|w~}M{|w~}M{|w~}M{|w~}M{|w~}M{|w~}W{|u~}m{}w~h{}v~O{}w~}m{}w~|a{}w~}m{}w~|a{}w~}m{}w~|a{}"
4230       "w~}m{}w~|Wv~Lv~Lv~Lv~V{}v~n{|v~_u~nv~|a{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~},{}w~}q{}t~}`"
4231       "{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`{|w~}ov~|_u~|o{|v~`{|w~}ov~| X{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|u"
4232       "u~|u~|v{|w~j{}x~|nu~|v{|w~j{}x~|b{|w~Zw~}Uv~|q{|v~ VZ~}c{}w~r{|y~}({}`~d{}^~|h{|Z~g{|_~}c{}w~l{|w~`w~}kw~}[{}w~"
4233       "|yv~|X{}w~|sv~|dV~}    2v~| k{}w~| {{|w~t{|w~Zs~y}y~|^{|o~|v{}x~}rx|e{|v~y}u~n{|w~},{|v~Fv~|Y{|~}tx~t{}~|U{}w~ "
4234       " dw~|Y{|v~k{}w~}W{}w~}Hu~Vp~}|Y{|w~}s{}w~}\\{|~}|q{}t~|`{|q~}|xy|t~|Rv~|U{|}p~|[{}v~|ot~} V{|}p~}|Z{|Z~}Z{|}p~}"
4235       "|W{}v~|b{}x~|s{}w~|s{|u~|tw~i{}w~}r{}w~}_{}g~}|`v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~O{}w~}V{}"
4236       "w~|w{|w~|v{}w~vv~|h{}w~|uv~|tv~iv~}c{|v~|e{}w~}sy|s~fv~}c{|v~|g{}f~}Z{}k~}S{|v~|Z{|v~h{}w~}b{|v~pv~|g{}w~}tw~|u"
4237       "w~|u{|v~_{}u~O{}t~|O{|u~|R{|w~}J{|w~|I{|w~|  aw~}^v~}m{}w~}`v~|P{|v~m{}v~|av~l{|w~}Xw~}V{|v~m{|v~|`v~}n{}w~|Wv~"
4238       "Lv~Tw~}w{}v~}Ov~_v~}o{|v~}o{|w~}hv~}n{}w~|av~|n{|v~|`u~mv~|bv~m{}v~|Zv~}R{}w~Gv~S{|w~|m{}w~|`{|v~ov~d{}w~|tw~|{"
4239       "w~|u{|w~}`v~}y{|v~|Z{}w~|q{|v~P{}v~|Sv~|Lw~|Lv~|W{|y}w~}|iy}5{|y~}sw~|x~}s{}y~|f{|v~|ps~^v~x{}q~}|W{|r~|y{|w~}`"
4240       "{}w~m{}v~^{}w~Mv~}n{}w~|^{}w~q{|v~Wv~y|w~}[{|w~|m{}w~|g{}v~b{}w~}h{|v~|v{}w~u{}w~}a{|w~}q{|w~}`v~t{}w~t{|w~}av~"
4241       "mv~|c{|v~|n{|v~W{|w~}W`~_{}w~}r{}w~}Q{|v~}X{}w~}V{|v~b{}w~}lv~}r{|v~r{|v~|i{}w~}hv~|h{}v~s{|v~s{|v~|kv~}xi~}y{|"
4242       "v~|ku~|?{}w~?{|u~|\\{}w~[{}v~}e{}v~|\\{}w~ H{}v~ty~}S{|v~P{|w~o{}w~_s|}r~s|Vw~|V{|w~r{|u~0{|y~v{}x~}d{|y~|d{}o~"
4243       "|x~}Y{}v~v{|v~|b{|Z~}8{|y~rw~u}v~|s{|y~| `{|Z~}a{}l~|X{|m~|'{|w~|m{}w~|^o~}w{|x~}W{|v~| xm~}W{|n~}X{|v~|vv~}e{}"
4244       "n~}v{}x~}o{|v~m{}x~|q{|w~w{|o~|t{|~}y|w{|}v~u{|x~}o{|v~3{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w"
4245       "~}r{}w~}\\v~|r{|w~}cv~|s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|uv~|tv~iv~}c{|v"
4246       "~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|a{|u~|}v~}av~}pw~}s{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}["
4247       "{}t~|W{}w~}mv~}aw~}v{}v~|Fw~}Lw~}Lw~}Lw~}Lw~}Lw~}Vu~l{|w~|iv~|Ov~l{|w~}av~l{|w~}av~l{|w~}av~l{|w~}Wv~Lv~Lv~Lv~V"
4248       "v~|mv~|`v~}n{}w~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|-v~|r{|x~}v~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{"
4249       "}w~|a{|w~|m{}w~|_{}w~|q{|v~^u~mv~|`{}w~|q{|v~ Ww~p{}w~pw~jw~yd|yw~jw~t{|p~|tw~jw~nu~|tw~jw~pv~}qw~Zw~|U{}w~}q{}"
4250       "w~} F{}w~}W{|w~|s{}y~|){|_~}f{}\\~|h{}\\~|g{}^~c{}w~l{|w~|aw~}kw~}[v~x{}w~}X{|w~}t{|v~cV~}    2v~| k{}w~| {{|x~"
4251       "}t{|x~}Z{|o~}y|`{|}r~|v{|w~t{}u~}|hv~}y{}u~o{|w~|,{|v~F{}w~|X{|sx~s{|T{}w~  e{|w~X{|v~k{}w~}W{}w~}Iu~|Vm~|[{}w~"
4252       "r{}w~}L{}u~`{|r~|s{|u~S{}v~V{|}m~}|\\u~p{}t~} Y{|}p~}|VY|W{|}p~}|[{|v~|aw~rw~}q{|v~|t{}x~iv~q{|v~_{}e~}av~}M{}w"
4253       "~}f{|v~|e{}d~|_{}g~|dv~}m{}n~|h{}^~|Y{}w~}M{}w~}W{}q~}P{}w~}V{}w~|vw~}vw~}vv~|h{}w~|u{}v~tv~iv~}bv~|e{}e~|fv~}b"
4254       "v~|g{}g~}X{|}k~}U{|v~|Z{|v~h{}w~}av~|r{|v~f{|v~u{|w~|u{}x~}u{}w~}`{|t~|O{}v~}Nu~|Q{|w~}Iw~}I{|w~|  a{}w~^v~|m{|"
4255       "w~}a{|v~O{|w~}lv~|b{|w~}kv~Xw~}V{|w~}lv~|`v~|n{|w~}Wv~Lv~Tw~}x{}v~|Nv~_v~|nv~|nv~hv~|n{|w~}b{|v~lv~|`v~}m{|w~}c"
4256       "{|w~}m{|v~|Zv~|R{}w~|Hv~S{|w~|m{}w~|_{}w~|q{|w~}d{|w~}u{|w~y{}x~|u{|w~|`{|v~y|v~}Y{|w~}q{}w~|Q{|v~}S{}v~Kw~|L{}"
4257       "w~}Y{|p~}|n{|y~}5{}y~r{|t~qy~}f{}v~ot~}^v~x{}o~}Y{}p~|{|w~|`w~}lv~|_{|w~}Nv~|n{|w~}^{|w~|r{}w~|X{}w~}yv~[{|w~|m"
4258       "{}w~|gv~}b{}v~h{|v~u{}w~u{|v~a{|w~}q{|w~}`v~t{}w~t{|w~}b{|w~}m{|w~}c{|v~lv~|X{|w~}W`~_v~|r{|v~Qu~W{}w~}V{|v~b{}"
4259       "w~}lv~}r{|v~qv~|i{}w~}hv~|h{|v~|t{|v~s{}v~jv~}xi~}xv~|lu~[|]{}w~\\\\|u~|]{}w~\\{}v~}c|u~|]{}w~ H{}w~}ty~}X{}g~|"
4260       "[{}x~}nw~Vs~|Nw~|V{}x~}pv~}1{}y~v{}x~}d{|y~}c{}r~}{|x~}Z{}w~}v{|v~|a{|Z~}8{}y~rn~}q{|y~} `{|Z~}a{}l~|X{|o~}|&{|"
4261       "w~|m{}w~|]{}q~}w{|x~}W{|v~| xm~}V{|}q~|V{|v~|v{}w~}fm~}vw~o{|u~rm~}vw~|w{}n~|u{|m~|uw~|p{|u~3v~q{|v~\\v~q{|v~\\"
4262       "v~q{|v~\\v~q{|v~\\v~q{|v~]{|v~pv~|e{}w~}r{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w"
4263       "~|u{}v~tv~iv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|`{|p~}`v~}q{}x~}qv~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}"
4264       "w~}Z{}v~}V{}w~}mv~}aw~}uu~}G{}w~L{}w~L{}w~L{}w~L{}w~L{}w~V{}w~}kw~}j{|v~O{|w~}kv~b{|w~}kv~b{|w~}kv~b{|w~}kv~Wv~"
4265       "Lv~Lv~Lv~W{|v~l{}w~}`v~|n{|w~}b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|.{|v~r{|w~{}w~|a{|w~|m{}w~|a{|w~|m{}"
4266       "w~|a{|w~|m{}w~|a{|w~|m{}w~|_{|w~}q{}w~|^v~}m{|w~}`{|w~}q{}w~| Ww~yd~|{w~jw~yd~|{w~jw~s{|r~|sw~jw~ou~|sw~jw~pv~}"
4267       "qw~Zw~|U{|v~qv~| G{}w~}Uw~}sx~({}^~g{}Z~g]~}f{|_~|cw~}l{|w~|aw~}kw~}\\{|v~x{|v~Wv~t{}w~}cV~}    2v~| k{}w~| {{}"
4268       "x~}t{}x~}Y{|}m~}`{|}w~}|tw~|v{|q~}j{}v~w{}u~p{}w~|,{|w~}F{}w~|Ox~Z{|Z~}  t{}x~}X{|v~k{}w~}W{}w~}J{}v~|Ut|}t~}]{"
4269       "|w~|r{}w~}K{}v~|a{|s~p{|v~}Tv~}W{}i~}]{}u~|t{|}s~} Z{|q~}| e{|}q~}\\v~}`x~}s{}w~ov~|t{}x~|k{|w~}p{}w~|`{}w~}p|}"
4270       "t~|cv~}M{}w~}f{|v~|e{}w~}i|^{}w~}l|cv~}m{}n~|h{}w~}h|v~|Y{}w~}M{}w~}W{}w~}u~}Q{}w~}V{}w~|v{}w~w{|w~uv~|h{}w~|tv"
4271       "~|uv~iv~}c{|v~|e{}f~|ev~}c{|v~|g{}i~}S{|}m~}V{|v~|Z{|v~h{}w~}a{}w~}rv~}ev~|v{|w~t{|w~uv~|`r~O{|v~|O{}v~}P{|w~}I"
4272       "{}w~I{|w~|  a{}w~^v~|lv~a{}w~}O{}w~|lv~|b{|w~|k{}w~Xw~}V{}w~|lv~|`v~m{|w~}Wv~Lv~Tw~}yu~|Mv~_v~mv~mv~hv~m{|w~}b{"
4273       "}w~}l{}w~}`v~|m{|v~c{}w~|lv~|Zv~Q{}v~|Iv~S{|w~|m{}w~|_{|w~}q{}w~|cv~u{}x~}y{}x~}u{}w~^{}q~}Wv~qv~Q{|v~}Uy|}v~|K"
4274       "w~|L{|u~}|^{|k~}|s{|}x~}5y~}q{}v~|q{}y~f{}w~}o{}u~|^v~ty|}s~[{|u~y}v~y|w~|a{|w~}l{}w~}^{}w~|Ov~m{|w~}]w~}rv~Wv~"
4275       "|y{}w~}\\{|w~|m{}w~|gv~|b{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{|w~}b{}w~|m{|v~c{}w~}l{}w~}X{|w~}W`~`{|w~}pv~|"
4276       "S{}v~|W{}w~}V{|v~bv~}lv~}r{|v~r{|v~|i{}w~}hv~|gu~t{|v~t{|v~}jv~}xh|y{|v~|mT~]{}w~]T~|^{}w~]{}U~|^{}w~ Hv~|ty~}X"
4277       "{}g~|[w~|nw~|W{}u~}Mw~|V{}w~ov~1{|y~v{}x~}d{|y~|ay}x~y}ww|[{}w~}v{|v~|`{|Z~}8{|y~ro~o{|y~| Q{}w~R{}l~|V{|y}v~y}"
4278       "|${|w~|m{}w~|\\{|}s~}w{|x~}W{|v~| xm~}T{|y}w~}|S{|v~|v{}w~}gm~}w{}x~}oy~y}x~rm~}w{}x~}v{}~}y|w{|v~u{|o~}t{}x~}o",
4279       "t~^v|V{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{}w~}p{}w~}ev~|r{|v~h|lv~}I{}w~}i|_{}w~}i|_{}"
4280       "w~}i|_{}w~}i|V{}w~}M{}w~}M{}w~}M{}w~}_v}u~r}nv~}h{}w~|tv~|uv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|"
4281       "_{|r~}_v~}r{}w~q{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}mv~}aw~}u{|t~|I{}w~L{}w~L{}w~L{}w~"
4282       "L{}w~L{}w~V{}w~|kv~j{}w~}O{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~Wv~Lv~Lv~Lv~W{}w~}l{|w~}`v~m{|w~}b{}w~}l{}"
4283       "w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}eY|f{}w~}rw~y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|"
4284       "m{}w~|^v~qv~]v~|m{|v~_v~qv~ Vw~yd~|{}x~|kw~yd~|{}x~|kw~r{|t~|r{}x~|kw~pu~|r{}x~|kw~pv~}q{}x~|[w~|T{}w~|s{|v~ G{"
4285       "}v~T{}w~t{|y~}(]~|i{|Y~}h{|_~}d{|a~}bw~}kw~|aw~}kw~}\\{}w~}wv~|Xv~|u{}w~|cV~}    2v~| k{}w~| {{w~|tw~|W{|}m~}T{"
4286       "}x~}v{|o~}l{|v~|v{}u~q{}w~+{|w~}F{}w~|Ox~Z{|Z~}+m| ww~|X{|v~k{}w~}W{}w~}K{}v~}K{|}v~}^w~}q{}w~}Ju~a{|t~|o{}v~U{"
4287       "|v~|X{}u~}|wy|u~}]t~}y|{y|}q~} Z{|t~}| _{|}t~}\\v~`{|x~}s{}x~}o{|w~|t{}x~|kv~|p{|w~}`{}w~}n{|u~cv~}M{}w~}f{|v~|"
4288       "e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|v{|w~|x{}x~}uv~|h{}w~|t{|v~uv~iv~}c{|v~|e{}h~"
4289       "}cv~}c{|v~|g{}h~}Qy|y}p~W{|v~|Z{|v~h{}w~}a{|v~s{|v~|e{}w~}v{}x~}t{|w~uv~|a{}r~}P{|v~|P{}v~}O{|w~}I{|w~|J{|w~|  "
4290       "n{|y}l~^v~kv~a{}w~|Ov~|l{}w~|b{}w~|k{}w~|Yw~}Vv~|l{}w~|`v~m{|w~}Wv~Lv~Tw~}|u~Kv~_v~mv~mv~hv~m{|w~}b{}w~|l{|v~`v"
4291       "~kv~c{}w~|l{}w~|Zv~Pu~}|Kv~S{|w~|m{}w~|^v~qv~b{}w~u{}x~|y{|w~uv~]{}r~V{}w~|s{|w~}R{|v~}X{|q~}Jw~|K{|q~}c{}g~}w|"
4292       "}u~}5y~}pw~}p{}y~fv~|o{}u~]v~p{|t~\\v~}w{|w~}w~|a{}w~|l{|w~}]{}w~}y|Rv~m{|w~}]{}w~s{}w~}X{}w~}x{|v~\\{|w~|m{}w~"
4293       "|h{|v~|b{|v~|i{}w~|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~kv~c{}w~|l{|w~}X{|w~}Wv~jv~`v~|p{}w~}T{}v~|V{}w~}V{|v~"
4294       "|cv~|lv~}r{|v~r{|v~|i{}w~}hv~|g{}v~}u{|v~tu~|jv~}c{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ I{|v~sy~}X{}g~|[w~m{}x~|Vu~"
4295       "|#{|w~|p{|w~|2{|y~|w{|x~}d{|y~|3v~}v{}v~|Aw~}8{|y~|sw~x{|w~}p{|y~| Q{}w~  p{|w~|m{}w~|Y{|}v~}w{|x~}W{|v~|  jv~}"
4296       "v{}v~|W{|w~o{}y~{}x~r{}n~}x{|w~uy|rw~|ty|t}|s{|w~o{}y~|}x~^{}w~|Wv~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|"
4297       "w~}^v~|p{|v~f{|v~q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|t{|v~uv~iv~}c{|v~|lv~}"
4298       "c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|^{|t~}^v~}s{}w~p{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w"
4299       "~}n{|v~|aw~}t{}t~}W{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~c{|y}l~j{}w~j{}w~|O{}w~|k{}w~|c{}w~|k{}w~|c{}w~|k{}"
4300       "w~|c{}w~|k{}w~|Xv~Lv~Lv~Lv~W{}w~|l{|v~`v~m{|w~}b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~f{|Z~}f{}"
4301       "w~|s{}x~|y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|^{}w~|s{|w~}]v~kv~_{}w~|s{|w~} Vw~yd~|{}x~|kw~yd"
4302       "~|{}x~|kw~qt~|r{}x~|kw~qu~|q{}x~|kw~pv~}q{}x~|[w~|T{|w~}s{}w~} H{|v~|T{|w~|u{}y~({|]~}i{}X~g{|`~b{}b~aw~}kw~}aw"
4303       "~}kw~}\\v~|w{}w~}X{}w~}uv~bw~}Z|    5x|v~}p| v{}w~| {|w~t{|w~S{|}n~|Vw~uv~|y{|}w~}m{}w~}t{}u~rw~}+{|w~}F{}w~|Ox"
4304       "~Z{|Z~},{|m~ x{|w~|X{|v~k{}w~}W{}w~}L{}v~}H{}v~}`{}w~p{}w~}J{}v~`t~n{|v~|V{}v~X{}v~}q{}v~}^{|j~|v~| Z{|t~| ]{|}"
4305       "u~}]{|w~}`{|x~|sw~|o{|w~|t{}x~|l{|v~nv~`{}w~}lv~}dv~}M{}w~}f{|v~|e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{"
4306       "}w~}{|t~S{}w~}V{}w~|u{}x~}y{|w~|uv~|h{}w~|sv~|vv~iv~}c{|v~|e{}k~}|av~}c{|v~|g{}w~}t|y}u~}M{|}s~}X{|v~|Z{|v~h{}w"
4307       "~}`v~}t{}v~d{}w~}vw~|sw~|w{|v~a{|v~}v~|Q{|v~|Q{|u~N{|w~}Hw~|J{|w~|  p{}h~|_v~k{}w~|bv~|Ov~k{}w~|bv~j}v~|Yw~}Vv~"
4308       "k{}w~|`w~}m{|w~}Wv~Lv~Tq~}Jv~_w~}mv~mv~hw~}m{|w~}bv~|l{|v~`v~kv~|dv~k{}w~|Zv~P{}r~}y|Pv~S{|w~|m{}w~|^{}w~|s{|w~"
4309       "}b{|w~|vw~|xw~|w{|w~}\\s~|Uv~sv~|Ru~W{|s~}|Iw~|I{|}t~}d{|u~}w|}g~}5{|y~|p{|x~|p{}y~fv~|o{|v~}]v~n{}v~|^{}w~|ts~"
4310       "`v~|l{|v~\\{}p~}Xw~}m{|w~}]{|w~|tv~|Xv~|wv~|]{|w~|m{}w~|h{|v~|q{}x~}q{|v~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{"
4311       "|w~}bv~kv~|dv~|l{|v~X{|w~}Wv~|l{|v~a{|v~nv~U{|v~}U{}w~}Uv~}d{|v~|l{}v~r{|v~r{|v~|i{}w~}hv~|fu~|v{|v~u{}v~}iv~}c"
4312       "{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ rw|V{|w~}sy~}X{|w}u~q}Zw~m{}x~|V{}v~\"{|v~ow~|2{|y~|w{|w~d{|y~|4{}w~}v{|v~?w~"
4313       "}8{|y~|sw~vw~}q{|y~| Q{}w~  p{|w~|m{}w~|Ux~}w{|x~}W{|v~|  i{}w~|v{|v~Ww~|p{|y~|{}x~`{}x~|j{|x~}bw~|p{|y~}{}x~^{"
4314       "}w~|X{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|w~}nv~|g{}w~}q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}"
4315       "M{}w~}Z{|v~ev~}h{}w~|sv~|vv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|]{}u~|^v~}t{|w~|p{|v~|i{|v~h{}w~}"
4316       "f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}n{}v~|aw~}s{|s~|[{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|f{}h~j}v~"
4317       "jv~|Ov~j}v~|cv~j}v~|cv~j}v~|cv~j}v~|Xv~Lv~Lv~Lv~Wv~|l{|v~`w~}m{|w~}bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v"
4318       "~f{|Z~}fv~|t{}x~|wv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]v~sv~|]v~kv~|_v~sv~| Vw~yd~|{w~jw~yd~|{w~j"
4319       "w~rr~|sw~jw~ru~|pw~jw~pv~}qw~Zw~|Sv~sv~ H{|v~|Rw~}uy~}({|]~}i{}X~|g{}b~|a{}d~|aw~}kw~}aw~}kw~}]{|v~v{|v~X{|v~v{"
4320       "|w~}b{}x~}     pf~ v{|w~ {{|w~t{|x~}P{|y~}r~W{}x~|v{}w~u{}w~mv~r{}u~t{|w~|+{|v~F{}w~|Ox~Z{|Z~},{|m~ x{}w~W{|v~k"
4321       "{}w~}W{}w~}M{}v~}F{}v~a{|w~|p{}w~}Iv~|au~}mv~}Vv~|Y{|v~}o{|v~|]{}m~|{v~| Z{|r~}| c{|}r~}]{|w~}`{|x~|sw~|nw~|t{}"
4322       "x~k{}w~}n{}w~}a{}w~}l{|v~|e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~mr|v~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|t~T{}w~}V{}w~|u"
4323       "{|w~y{}w~tv~|h{}w~|s{|v~vv~i{}v~c{|v~|e{}w~}r|]{}v~c{|v~|g{}w~}q{}v~}K{|t~|Y{|v~|Z{|v~h{}w~}`{}v~tv~|d{|v~w{|w~"
4324       "|s{}x~}w{}w~}av~}{}v~Q{|v~|R{|u~M{|w~}H{}x~}J{|w~|  r{|f~|_w~}k{}w~|bv~|Ov~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~"
4325       "Lv~Tq~Iv~_w~}mv~mv~hw~}m{|w~}bv~jv~`v~k{}w~|dv~k{}w~|Zw~}O{}o~}|Sv~S{|w~|m{}w~|^{|w~}s{}w~|b{|w~}w{|w~w{}x~}w{|"
4326       "w~|\\{|u~}T{}w~|u{|w~}Ru~V{|s~}|Iw~|J{|}s~}d{|w~|s{|}k~|3y~}p{|x~}p{}y~fv~mv~|]v~m{}v~_{|w~}rt~`v~jv~Z{}r~}Xw~}"
4327       "m{|w~}\\w~}u{|w~}X{|w~}v{}w~}]{|w~|m{}w~|h{|v~|q{}x~}pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~jv"
4328       "~X{|w~}W{}w~|l{|v~a{}w~}n{}w~}W{|u~T{}w~}U{}w~}d{}v~k{}v~|s{|v~r{}v~h{}w~}hv~|f{|u~|w{|v~v{}u~h{}v~c{|v~|n{|T~]"
4329       "{}w~]T~|^{}w~]{}U~}^{}w~ s{|w~V{|w~}sy~}S{|v~Pw~|nw~|V{|w~}!{}v~|q{}x~|1y~}vw~|e{}y~ci|]{}w~u{|w~|?w~}7y~}sw~v{"
4330       "|w~|r{}y~ P{}w~  p{|w~|m{}w~|Ux~}w{|x~}W{|v~| Fi|U{|w~|u{}w~X{}x~}p{|y~}y{}x~a{|w~i{|x~}c{}x~}p{|y~}y{}x~^{}w~|"
4331       "X{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~|n{}w~}h{|v~p{|v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}"
4332       "D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|s{|v~vv~i{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|^{}s~|_"
4333       "{}v~u{|w~|o{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}o{|u~`w~}q{}t~|^{|f~|^{|f~|^{|f~|^{|f~|"
4334       "^{|f~|^{|f~|h{|P~jv~|O`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~|u{}x~}"
4335       "vv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]{}w~|u{|w~}\\v~k{}w~|_{}w~|u{|w~} Uw~yq}w~r}yw~jw~yd|yw~jw~"
4336       "sp~|tw~jw~su~|ow~jw~pv~}qw~Zw~|S{}w~}u{|w~} Hv~|Q{}w~|w{|y~|({|\\~iW~|f{}d~|_e~|`w~}kw~}aw~}kw~|]{}w~}uv~Wv~|w{"
4337       "}w~|b{}x~}     q{|g~| v{|w~({}Z~X{|y~|{|}u~}Y{|w~uw~|tw~}o{|w~}q{}u~u{}w~*{|v~F{}w~|*m|}w~l|,{|m~ xw~}W{|v~k{}w"
4338       "~}W{}w~}N{}v~}Dv~|bw~}o{}w~}Iv~|au~|m{}w~}W{|v~X{}v~m{}v~\\{|p~}xv~| Y{}p~}| i{|}p~}|]{}w~}`{|x~|sw~mw~|t{}x~kv"
4339       "~}n|}v~a{}w~}kv~}e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~dv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}x{|t~U{}w~}V{}w~|tw~|{w~}tv~|"
4340       "h{}w~|rv~}wv~i{}v~c{}v~d{}w~}T{}v~c{}v~f{}w~}p{}v~|Ju~}Y{|v~|Z{|v~h{}w~}_v~|v{|v~bv~|x{|w~r{}w~wv~|b{}v~xv~}R{|"
4341       "v~|Ru~|M{|w~}H{|w~J{|w~|  s{|q~t}v~|_w~}k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tp~Jv~_w~}mv~mv~hw~}"
4342       "m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}N{|m~|Uv~S{|w~|m{}w~|]v~t{|v~`v~w{}x~}w{|x~}w{}w~[{|u~|T{|w~}u{}w~|S{}v~|V{|"
4343       "x}t~}Jw~|K{|s~y}|d{|y~}n{|}p~}1y~}p{}w~p{}y~fv~mv~\\v~lv~|`{}w~|r{|v~}`v~jv~\\{|p~}Xw~}m{|w~}\\{}w~u{}w~|Xv~|v{"
4344       "|v~]{|w~|m{}w~|h{|v~p{}w~pv~}iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~|l{|w~}av~|n{|v"
4345       "~Wu~|T{}w~}U{}v~dv~}k{|v~}s{|v~s{|v~}h{}w~}hv~|e{}u~|x{|v~w{}u~|h{}v~c{}v~l{|u~}\\|]{}w~][|u~|]{}w~\\{}v~}c|u~}"
4346       "]{}w~ s{|w~V{|w~}sy~}S{|v~P{}x~}o{|w~`{|a~}+u~|rw~|1y~}v{}w~ex~d{|j~}]{}w~}v{|v~|@w~}7y~}sw~u{}w~rx~ P{}w~  p{|"
4347       "w~|m{}w~|Ux~}w{|x~}  w{|j~}V{|v~|v{}w~}Xw~oy~}x{}x~aw~|i{|x~|cw~ox~x{}x~^{}w~|Xv~}n|}v~`v~}n|}v~`v~}n|}v~`v~}n|"
4348       "}v~`v~}n|}v~a{|b~h{}v~p|}v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|rv~}wv~i{}v~c{"
4349       "}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~^{}q~|`{}v~v{|w~}n{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{"
4350       "|v~|V{}w~}p{|u~|`w~}p{|t~}`{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|i{|q~t}`~|kv~N`~|c`~|c`~|"
4351       "c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~u{|x~}uv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m"
4352       "{}w~|a{|w~|m{}w~|]{|w~}u{}w~|\\w~}k{}w~|_{|w~}u{}w~| U{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|uu~|u~|v{|w~j{}x~|uu~|o{|"
4353       "w~j{}x~|qv}|r{|w~[{|w~|S{|v~uv~| TZ~}a{|w~}wx~'{|\\~iW~|ee~|^{|g~}_w~}kw~}aw~}kw~|]v~|u{}w~|X{}w~}wv~|b{|w~|   "
4354       "  r{}g~ u{|w~({}Z~X{|y~|w{}v~|Zw~|v{|w~s{|w~o{|w~}p{}u~vw~})v~Fv~| w{}w~ x{|m~ y{|w~|Vv~|lv~|W{}w~}O{}v~}C{}w~}"
4355       "c{|w~n|}w~}v|N{}w~}au~l{|v~Wv~}Xv~}m{|v~|[{|y}w~y}|x{|v~ V{|}p~}|XY|X{|}q~}|Z{}w~}`{|x~|sw~mw~|tw~l{|b~|b{}w~}k"
4356       "{}v~e{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}w{|t~V{}w~}V{}w~|t{}w~|w~|tv~|h{}w~|r{|v~"
4357       "wv~i{|v~|d{}v~d{}w~}T{|v~|d{}v~f{}w~}o{}v~J{|u~Y{|v~|Z{|v~h{}w~}_{}w~}v{}w~}b{}w~}x{}x~}r{|w~wv~b{|v~|x{|v~|S{|"
4358       "v~|S{}v~|L{|w~}Gw~|K{|w~|  t{|u~}|q{}w~|_v~k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}|u~Kv~_w~}mv~"
4359       "mv~hw~}m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}L{|}o~}Vv~S{|w~|m{}w~|]{}w~}u{}w~}`{}w~|xw~|w{|w~wv~\\{|s~Sv~uv~S{}v~"
4360       "|O{}v~}Kw~|L{|v~}|_{|~|j{|y}x~y}|/x~q{|v~}qx~fv~m{}x~}\\v~l{}w~|`v~pv~}`v~jv~]n~}Xw~}m{|w~}\\{|w~|vv~X{|v~t{}w~"
4361       "|^{|w~|m{}w~|h{|v~p{}w~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~}l{}w~}b{|v~lv~|Y"
4362       "{}v~|S{}w~}U{|v~}f{|v~|ju~|t{|v~s{}v~|h{}w~}hv~|dt~}y{|v~y{|t~|g{|v~|d{}v~k{|u~|?{}w~>u~|b{|v{}w~[{}v~|e{}v~}\\"
4363       "{}w~ s{|w~V{|w~}sy~}S{|v~P{|w~o{}x~}`{|a~}+{|u~}|u{|w~0{}y~v{|w~}g{|y~}d{|j~}\\{}v~|w{|v~}Aw~}7{}y~sw~tw~}t{|y~"
4364       "} P{}w~  p{|w~|m{}w~|Ux~}w{|x~}  w{|j~}W{|v~|vv~}X{}x~|p{}y~|x{}x~b{}x~}hw~c{}x~}p{}y~|x{}x~^v~X{|b~|b{|b~|b{|b"
4365       "~|b{|b~|b{|b~|b{}b~}id~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|r{|v~wv~i{|v~|d{}v"
4366       "~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~_{}v~}u~|a{|v~|ww~}m{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w"
4367       "~}Z{|v~|V{}w~}sy|s~_w~}n{}u~|b{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|j{|u"
4368       "~}|q{}a~|kv~N`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~.v~v{|w~tv~a{|w~|m{}w~|a{"
4369       "|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|\\v~uv~[w~}k{}w~|^v~uv~ T{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|{|u~|w{|x~}j{}"
4370       "x~}vu~|n{|x~}j{}x~}b{|x~}[{|w~Qv~|w{|v~ SZ~}`v~x{|y~}'{|]~}iW~|e{|g~}\\{}i~}^w~}kw~}aw~}l{|w~|^{|v~t{|w~}X{|v~x"
4371       "{|v~`w~}     m{|v~ jw|({}Z~X{|y~|v{}w~}[{}x~}u{}x~}s{|w~o{}w~}o{}u~x{|w~|)v~Fv~ v{}w~  g{}w~Uv~|lv~|W{}w~}P{}v~"
4372       "}B{|v~c{|_~|O{}w~}a{}v~l{|v~X{|v~|Y{|v~|lv~|N{|v~ S{|}p~|[{|Z~}[{|}p~}|X{}w~}`{|x~|sw~|nw~|u{|x~}l{}b~}b{}w~}k{"
4373       "|v~e{|v~}N{}w~}g{|v~}d{}w~}L{}w~}T{|v~}ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}v{|t~W{}w~}V{}w~|t{|r~sv~|h{}w~|q{}w~}xv"
4374       "~i{|v~}dv~}d{}w~}T{|v~}dv~}f{}w~}nv~}J{}v~Y{|v~|Z{|v~|i{}w~}_{|v~vv~|b{}w~}xw~|qw~|y{|v~bv~}v{}v~S{|v~|T{}v~}K{"
4375       "|w~}G{}x~}K{|w~|  tv~}n{}w~|_v~kv~|bv~|Ov~k{}w~|bv~Bw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}{|u~|Mv~_w~}mv~mv~hw~}m{|w~"
4376       "}bv~|kv~`v~k{}w~|dv~k{}w~|Zw~}Iy|}q~Wv~S{|w~|m{}w~|]{|v~uv~_{|w~|xw~uw~|y{|w~}\\r~}T{|w~|w{}w~}T{}v~|M{|v~Kw~|L"
4377       "{}w~} O{}y~|rt~|s{|y~}fv~|nw~}\\v~l{|w~}`w~}p{}w~|`v~|kv~^u~}|Qw~}m{|w~}[w~}w{}w~}X{}w~|t{|w~}^{|w~|m{}w~|h{|v~"
4378       "pv~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~|l{|v~X{|w~}W{|w~}l{}w~|b{}w~}l{}w~}Z{|v~}R{}w~}T{}v"
4379       "~f{}v~i{|u~t{|v~t{|u~g{}w~}hv~|cr~}v~}s~}f{|v~}dv~}j{|u~|@{}w~?u~|b{}~|w{}w~vy~a{}v~|g{}v~}b{}~|w{}w~vy} {{}w~|"
4380       "W{|w~}sy~}S{|v~Ow~}q{|w~|`{|a~}){}u~}vw~}0{|y~}v{}w~}p{|t{}y~|d{|j~}[{|v~|vv~}Bw~}7{|y~}tw~t{|w~|u{}y~| P{}w~  "
4381       "p{|w~|m{}w~|Ux~}w{|x~}  w{|j~}X{}v~v{|v~}X{|w~p{|y~|w{}x~bw~h{}x~|d{|w~p{|y~}w{}x~^v~X{}b~}b{}b~}b{}b~}b{}b~}b{"
4382       "}b~}b`~j{}d~Y{|v~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fv~}g{}w~|q{}w~}xv~i{|v~}dv~}k{|v~}dv~}k"
4383       "{|v~}dv~}k{|v~}dv~}k{|v~}dv~}`{}v~|{|u~|b{|v~}x{}x~}lv~}h{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}Z{|v~|V"
4384       "{}e~|_w~}m{|u~bv~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|jv~}n{}w~Tv~|Ov~Lv~Lv~Lv~Av~Lv~Lv~Lv~"
4385       "Wv~|l{|v~`w~}m{|w~}bv~|kv~bv~|kv~bv~|kv~bv~|kv~bv~|kv~.v~vw~|u{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}"
4386       "w~|\\{|w~|w{}w~}[v~k{}w~|^{|w~|w{}w~} T{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~|x{|u~|x{}x~|j{|w~wu~|m{}x~|j{|w~b{}x~"
4387       "|[{|w~Q{|w~}w{}w~} SZ~}`{}w~|y{}y~|'{|n~y}~|n~}i{}k~x}k~c{|i~}Z{}j~]w~}kw~}a{}w~l{|w~|^{}w~}sv~Wv~|y{}w~}`{}w~|"
4388       "     mv~|  o{}Z~X{|y~|v{|w~}\\{|w~t{}x~|rw~|p{}w~}n{}u~yw~}(v~|Gv~ v{}w~  gw~}U{}w~}m{|v~V{}w~}Q{}v~}A{|v~c{|_~"
4389       "|O{}w~}a{}v~l{|v~X{}v~X{|v~k{}w~}N{}w~} Q{|}p~}|^{|Z~}^{|}p~}|U{}w~}`{|x~}sw~|o{|w~|u{}x~|l`~b{}w~}k{|v~|eu~N{}"
4390       "w~}g{}v~|d{}w~}L{}w~}Su~ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}u{|t~X{}w~}V{}w~|ss~}sv~|h{}w~|q{|v~|yv~hu~e{|v~|d{}w~}"
4391       "Su~e{|v~|f{}w~}n{}v~|K{|v~|Z{|v~|Yv~|i{}w~}^v~|x{}v~a{|v~y{|w~|q{}x~}y{}w~}c{}v~tv~}T{|v~|U{|v~}J{|w~}G{|w~K{|w"
4392       "~|  u{|v~m{}w~|_v~kv~a{}w~|O{}w~|l{}w~|bv~|Cw~}V{}w~|l{}w~|`w~}m{|w~}Wv~Lv~Tw~}y{|u~|Nv~_w~}mv~mv~hw~}m{|w~}bv~"
4393       "|l{|v~`v~kv~cv~|l{}w~|Zw~}D{|}u~}Xv~S{|w~|m{}w~|\\{}w~|w{|w~}^w~}y{|w~u{}x~}y{}w~|]{}q~|Tv~wv~|U{|v~}K{}w~|Lw~|"
4394       "Lv~ N{|x~s{}x~{w~|tx~|fv~|o{|v~\\v~l{|w~}a{|w~|p{}w~_{}w~|l{|v~_{}v~|Ow~}m{|w~}[{}w~|xv~X{|v~rv~|_{|w~|m{}w~|h{"
4395       "|v~|qv~pv~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~|bv~kv~c{}w~|l{|v~X{|w~}Vv~l{}w~|bv~|l{|v~[{|v~}Q{}w~}T{|v~}"
4396       "h{|v~|hu~}u{|v~u{|u~|g{}w~}hv~|b{}f~|du~e{|v~|i{|u~|A{}w~@u~|b{}x~|x{}w~ww~a{}v~|i{}v~}b{}x~|x{}w~w{}y~} {}w~|W"
4397       "{|v~sy~}S{|v~O{|w~}s{}w~}^q|}v~q|'{}t~|{|w~}.x~u{}v~}|wy|}y~tx~/{|v~|v{}w~}Cw~}6x~tw~s{}w~ux~ O{}w~  p{|w~|m{}w"
4398       "~|Ux~}w{|x~}  B{}w~}v{|v~|Ww~|q{|y~}v{}x~c{}x~|i{}x~}cw~|q{|y~}v{}x~_{|v~X`~b`~b`~b`~b`~c{|`~|kc~Xu~J{}w~}M{}w~"
4399       "}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~g{|v~}g{}w~|q{|v~|yv~hu~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|a{}"
4400       "v~|x{|u~|bu~y{}w~l{|v~|gv~|i{}w~}ev~|i{}w~}ev~|i{}w~}ev~|i{}w~}Z{|v~|V{}f~|^w~}l{|v~|d{|v~m{}w~|a{|v~m{}w~|a{|v"
4401       "~m{}w~|a{|v~m{}w~|a{|v~m{}w~|a{|v~m{}w~|k{|v~m{}w~T{}w~|Ov~|Mv~|Mv~|Mv~|Bv~Lv~Lv~Lv~W{}w~|l{|v~`w~}m{|w~}bv~|l{"
4402       "|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~.v~|x{}x~|t{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|[v~wv~|[v"
4403       "~kv~\\v~wv~| Sw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|yu~|m{|w~hw~|d{|w~Z{|w~Pv~wv~| SZ~}_w~}yx~%n~{|~{|o~|"
4404       "i{|l~}|}|l~}b{}j~Xk~|]w~}kw~}a{}w~l{}w~]v~|s{}w~|X{}w~}yv~|_w~}     mv~}  g{}x~}t{}x~}O{|y~|uw~}\\{}x~|t{}x~|rw"
4405       "~|p{|w~}m{}u~}w~|({}w~|H{|w~} v{}w~  h{|w~|U{}w~}m{|v~V{}w~}R{}v~}@{|v~c{|_~|Ov~|a{|v~l{}w~}Xv~}X{|v~k{}w~}Nv~|"
4406       " N{|}p~}|a{|Z~}a{|}p~}|R{|w}|_x~}s{}x~}o{}w~|v{|w~l{}`~|c{}w~}k{|v~|e{}v~|O{}w~}gu~c{}w~}L{}w~}S{}v~|fv~|h{}w~}"
4407       "hv~|Y{}w~}M{}w~}W{}w~}t{|t~Y{}w~}V{}w~|s{}t~rv~|h{}w~|p{}w~}yv~h{}v~|f{}v~c{}w~}S{}v~|f{}v~e{}w~}mv~}K{|v~|Z{|v"
4408       "~|Yv~|iv~|^{}w~}xv~}`v~|{|w~p{}w~yv~|d{|v~|t{|v~|U{|v~|V{|u~I{|w~}Fw~|L{|w~|  u{}w~|mv~|_v~|m{|v~a{}w~}O{}w~|lv"
4409       "~|b{}w~|Cw~}V{}w~|lv~|`w~}m{|w~}Wv~Lv~Tw~}x{|u~|Ov~_w~}mv~mv~hw~}m{|w~}b{}w~|l{|w~}`v~kv~c{}w~|lv~|Zw~}B{|u~Xv~"
4410       "S{|w~|m{}w~|\\{|w~}w{}w~|^v~y{}x~}u{|x~}y{}w~]{|v~|}v~T{}w~|y{|w~}U{|v~}J{|w~}Lw~|M{|w~} Mx~}v{|w~|{|w~|v{}x~e{"
4411       "}w~|o{}v~\\v~l{|w~}a{|w~|pw~}_{}w~|l{|w~}_v~Mw~}m{|w~}[{|w~}y{|w~}X{}w~|r{}w~}_{|w~|m{}w~|h{|v~|qv~|r{|v~|i{}w~"
4412       "|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{}w~|bv~kv~c{}w~|l{|w~}X{|w~}Vv~lv~b{}v~jv~|\\u~P{}w~}S{}v~|iu~g{|t~|w{|v~v{}u~}"
4413       "f{}w~}hv~|a{}h~|c{}v~|f{}v~g{|u~|B{}w~Au~|b{}v~|y{}w~xu~a{}v~|k{}v~}b{}v~|y{}w~x{}w~}!{}w~|Vv~sy~}S{|v~O{|u~}y|"
4414       "{y|u~}T{|w~}Lw}|P{|}p~}-{|y~}u{}l~u{}y~|.{|v~|v{}w~}Dw~}6{|y~}uw~rw~}w{}y~| O{}w~  p{|w~|m{}w~|Ux~}w{|x~}  C{}w"
4415       "~}v{|v~|W{}x~}px~u{}x~d{|w~i{}x~}c{}x~}px~u{}x~_{}w~}Y{}`~|d{}`~|d{}`~|d{}`~|d{}`~|d{}w~}j|}w~}l{|c~X{}v~|K{}w~"
4416       "}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|p{}w~}yv~h{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~"
4417       "i{}v~|f{}v~a{}v~|v{|u~|c{}v~|}w~|l{}v~fv~|iv~|ev~|iv~|ev~|iv~|ev~|iv~|Z{|v~|V{}h~}\\w~}k{}w~|d{}w~|mv~|a{}w~|mv"
4418       "~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|k{}w~|mv~|U{}w~}O{}w~|M{}w~|M{}w~|M{}w~|Bv~Lv~Lv~Lv~W{}w~}l{}w~}`w~}m"
4419       "{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}.{}w~|y{}x~|s{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w"
4420       "~|m{}w~|a{|w~|m{}w~|[{}w~|y{|w~}Zv~kv~\\{}w~|y{|w~} R{|w~r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~y{|w~|lw~}"
4421       "h{|w~dw~}Z{|w~P{}w~|y{|w~} Rs}v~g}|_{}w~{|y~}%{|p~|{|~yp~}g{}m~{}~{}m~|a{}l~|X{|m~}\\w~}kw~}a{|w~|mv~]v~r{}w~}X"
4422       "{|v~{|v~^{}w~}     n{}v~  gw~|tw~|O{|y~|uw~}]{|x~}sw~|rw~|p{|v~l{}r~}'{|w~}H{|w~} v{}w~  h{|w~T{|v~m{}w~}V{}w~}"
4423       "S{}v~}?{|v~c{|_~|Ov~|`v~|m{}w~}Y{|v~W{|v~k{}w~}O{|v~ J{|}p~}|d{|Z~}d{|}p~}|-w~s{|w~ov~|v{}x~|lv~|j{|v~c{}w~}k{}"
4424       "v~cv~}O{}w~}h{}v~|c{}w~}L{}w~}Rv~}fv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}rt~Z{}w~}V{}w~|ru~}rv~|h{}w~|p{|v~|{v~h{|v~}g"
4425       "{|v~}c{}w~}Rv~}g{|v~}e{}w~}m{|v~|L{|v~|Z{|v~|Y{}w~}j{|v~|^{|v~|{|v~_{}w~}{}x~}p{|w~yv~cv~}r{}v~U{|v~|W{|u~|I{|w"
4426       "~}F{}x~}L{|w~|  u{}w~|n{|v~|_v~}m{}w~}a{|w~}O{|w~}m{|v~|b{}w~}Cw~}V{|w~}m{|v~|`w~}m{|w~}Wv~Lv~Tw~}vu~|Pv~_w~}mv"
4427       "~mv~hw~}m{|w~}b{|w~}l{}w~}`v~|m{|w~}c{|w~}lv~|Zw~}@v~|Yv~S{|w~}mv~|[v~wv~]{}w~|{w~|u{|w~yw~}]v~}y{}v~U{|w~}y{}w"
4428       "~|V{|v~}I{|w~}Lw~|M{|w~} M{|w~x}v~}x{}v~x}w~|e{}w~}ou~|]v~l{|w~|a{|w~p{}w~|_{|w~}l{}w~}`{|w~}Mw~}m{|w~}Zv~y{}w~"
4429       "|Y{|v~q{|v~_{|w~}m{}w~|gv~|r{|v~|r{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{}w~|bv~|m{|w~}c{|w~}l{}w~}X{|w~}V{}w~"
4430       "|n{|w~}bv~}j{}v~]{}v~|P{}w~}Ru~j{}v~|f{|t~}|y{|v~x{|t~}e{}w~}hv~|`{|}l~}`v~}g{|v~}f{|u~|C{}w~Bu~|`u~|{}w~yu~|`{"
4431       "}v~|m{}v~}a{|u~|{}w~y{}v~}!{}w~|Vv~|ty~}S{|v~P{|g~}U{|w~}Lw~|N{|r~}+{}y~|u{|}o~}v{|y~}+v~}v{}v~Ew~}5{}y~|vw~r{|"
4432       "w~|y{|y~} N{}w~  p{|w~}m{}w~|Ux~}w{|x~}  Dv~}v{}v~|W{|w~p{}y~|u{}x~dw~|j{}w~c{|w~p{}y~|u{}x~`{}v~|Yv~|j{|v~dv~|"
4433       "j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~l{}w~}n{|v~Wv~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{"
4434       "|v~}f{}w~|p{|v~|{v~h{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}b{}v~|t{|u~|cq~|l{|v~}f{}w~}j{|v"
4435       "~|e{}w~}j{|v~|e{}w~}j{|v~|e{}w~}j{|v~|Z{|v~|V{}k~}|Zw~}k{}w~}d{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{"
4436       "}w~|n{|v~|a{}w~|n{|v~|k{}w~|n{|v~}U{|w~}O{}w~}M{}w~}M{}w~}M{}w~}Bv~Lv~Lv~Lv~W{|v~lv~|`w~}m{|w~}b{|w~}l{}w~}b{|w"
4437       "~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}Xt|X{}w~}{}x~}r{}w~}a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|[{|w~}y"
4438       "{}w~|Zv~|m{|w~}\\{|w~}y{}w~| Qw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}y{|y~|l{}w~fw~}f{}w~Y{|w~P{|v~y{}w"
4439       "~| Kv~}J{|w~|}y~|${}r~}y{}~y{|q~f{|n~|{}~yn~}_m~|V{|o~}[w~}kw~}`w~}n{|w~}^{|w~}r{|v~Wv~{}w~}]v~|     o{|v~|  hw"
4440       "~t{|w~N{|y~|uw~}]w~|s{}x~|rw~|ov~|l{}s~&{|w~}H{}w~| v{}w~  h{}x~}Sv~|nv~|V{}w~}T{}v~}>{}w~}Q{}w~}J{}v~_{}w~}mv~"
4441       "}Y{}w~}Vv~|lv~|Ov~} G{|}p~}|0{|}o~}*{}x~rw~}q{}v~|w{}w~l{|v~hv~|d{}w~}ku~c{}v~}P{}w~}i{}u~b{}w~}L{}w~}R{}v~|gv~"
4442       "|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}qt~[{}w~}V{}w~|r{}v~|rv~|h{}w~|o{}w~}{v~g{}v~|hu~|c{}w~}R{}v~|hu~|e{}w~}lv~}L{}v~Y"
4443       "{|v~|Y{}v~j{}v~\\{}w~}{}w~}_{|v~{w~|ow~y|v~d{}v~pv~}V{|v~|Wu~|H{|w~}F{|w~L{|w~|  u{}w~m{}v~|_u~mv~|a{|v~Nv~|n{}"
4444       "v~|b{|v~Cw~}Uv~|n{}v~|`w~}m{|w~}Wv~Lv~Tw~}uu~|Qv~_w~}mv~mv~hw~}m{|w~}b{|v~lv~|`v~}m{}w~}c{|v~m{|v~|Zw~}@{}w~|Yv"
4445       "~S{|w~}mv~|[{}w~|y{|w~}]{|w~}{w~sw~y|w~}^{}w~}wv~}U{}w~yv~Uv~}Gw~}Lw~|M{|w~| L{|q~}v{}q~|d{|w~}p{|u~|]v~l{}w~|a"
4446       "{|w~pv~^{|v~lv~|`{|w~|Mw~}m{|w~}Z{}w~y|v~X{}w~}pv~|`{|w~}mv~|gv~}r{|v~}r{}v~h{|v~u{}w~u{|w~}a{|w~}q{|w~}`{}w~|u"
4447       "{}w~tv~av~}m{}w~}c{|v~lv~|X{|w~}V{|w~}n{}w~|c{|v~i{|v~|_{}v~}O{}w~}R{|v~}l{}v~|d{|r~y}v~y}s~}d{}w~}hv~|]{|}s~y}"
4448       "|^{}v~|hu~|e{|v~}C{}w~C{}v~|^u~|}w~{}v~|^{}v~n{|v~}_{|u~|}w~{}v~} {}w~|V{}w~}ty~}S{|v~Q{}e~}V{|w~}Lw~|L{|t~*{|x"
4449       "~|t{|y}u~}|u{|x~|*{}w~|v{|v~Fw~}5{|x~|ww|qw|y{|x~|   >{|w~}mv~|Ux~}w{|x~}  Ev~}v{|v~U{}x~|q{|y~}t{}x~e{}x~}j{}w"
4450       "~b{}x~|q{|y~}t{}x~a{}v~}Y{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{}v~hv~|n{|v~|n{|v~W{}v~}L{}w~}M{}w~}M{}w"
4451       "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~|o{}w~}{v~g{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|c{}"
4452       "v~|r{|u~|d{}s~|ku~|f{}v~j{}v~d{}v~j{}v~d{}v~j{}v~d{}v~j{}v~Y{|v~|V{}w~}r|Vw~}k{|w~|d{}w~m{}v~|a{}w~m{}v~|a{}w~m"
4453       "{}v~|a{}w~m{}v~|a{}w~m{}v~|a{}w~m{}v~|k{}w~m{}u~U{|v~O{|v~M{|v~M{|v~M{|v~Bv~Lv~Lv~Lv~Vv~|n{|v~_w~}m{|w~}b{|v~lv"
4454       "~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|X{}u~X{|v~|x~}qv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|Z{}w~yv~Yv~}m{}"
4455       "w~}[{}w~yv~ P{|w~}t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}yy|l{|w~}f{|w~}h{|w~}Y{|w~Ov~y|v~ K{}w~}Hw~}y"
4456       "~}\"{}t~}x{}~x{|s~d{|p~}y{}~y{|p~}]o~|T{}p~Zw~}kw~}`{}w~|o{}w~|^{}w~|qv~|X{}w~|v~|]{|v~|     o{}v~j{}  {|x~}t{|"
4457       "x~}N{|y~|v{}w~}^{}x~}r{}x~}rw~|o{}v~k{}u~|%v~Hv~ u{}w~  hw~|S{}v~o{}v~U{}w~}U{}v~}>{|v~}Q{}w~}Ju~_{|v~n{|v~|Z{|"
4458       "v~|Vv~}m{|v~|P{}v~ C{}o~}4{|o~}|({|x~}s{}w~}s{}u~|x{}w~|l{}w~}h{}w~}d{}w~}l{|v~}bu~|g{|}g{}w~}j{}u~|b{}w~}L{}w~"
4459       "}R{|u~|hv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}pt~\\{}w~}V{}w~|r{|v}qv~|h{}w~|nv~|v~g{|u~|j{}v~}b{}w~}R{|u~i{}v~}d{}w~}"
4460       "l{|v~|M{}v~Y{|v~|Y{|v~|kv~}\\{|v~{v~|_{|v~|w~|o{}x~y}w~}e{|v~|p{|v~|W{|v~|X{}v~}G{|w~}F{|w~|M{|w~|  u{}w~|nu~|_"
4461       "u~}o{}v~_v~}O{}w~}o{|u~|av~}Dw~}U{}w~}o{|u~|`w~}m{|w~}Wv~Lv~Tw~}t{}v~|Rv~_w~}mv~mv~hw~}m{|w~}av~|n{|v~_u~mv~|bv"
4462       "~|n{}v~|Zw~}@{}w~|Yv~Rv~n{}v~|[{|w~}y{}w~|\\w~}|x~}s{}x~y}w~|_{}v~v{|v~|V{|w~y}w~}Vu~Fw~}Lw~|M{|w~| K{|s~}t{}s~"
4463       "|bv~p{}u~}]v~|mv~`{|w~q{}w~}]v~}n{}v~_{|w~|Mw~}m{|w~}Yw~y}w~|Xv~o{|w~}`{|v~n{|v~|g{}v~r{}u~rv~}gv~|v{}w~uv~|a{|"
4464       "w~}q{|w~}`{|w~}u{}w~u{|v~au~mv~|bv~}n{}v~Vv~Uv~nv~b{}w~}hv~}`{|v~}N{}w~}Q{|v~}n{}v~}b{|c~}c{}w~}hv~|Z{|v~Z{|u~|"
4465       "j{}v~}c{|w~B{}w~B{}x~|\\u~}w~}v~|\\{}x~|m{}x~}]{|u~}w~}v~} {{v~|V{|v~|uy~}S{|v~R{}v~y|q~}|u~W{|w~}Lw~|J{}u~*{|x"
4466       "~|e{|x~|({}x~}u{|w~F{|x}|4{|x~|e{|x~|   ={|v~n{|v~|Ux~}w{|x~}  Ew~|u{|x~}U{|x~}p{}j~}iw~j{}w~b{|x~}p{}j~}f{}v~}"
4467       "X{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}fv~}h{}w~}n{}w~}m{|v~Vu~|g{|}c{}w~}M{}w~}M{}w~}M{}w"
4468       "~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~|nv~|v~g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}c{"
4469       "}v~|p{|u~|e{|t~}k{}v~}e{|v~|kv~}d{|v~|kv~}d{|v~|kv~}d{|v~|kv~}Y{|v~|V{}w~}Mw~}k{}w~|d{}w~|nu~|a{}w~|nu~|a{}w~|n"
4470       "u~|a{}w~|nu~|a{}w~|nu~|a{}w~|nu~|k{}w~|nt~|Uv~}Ov~}Mv~}Mv~}Mv~}Cv~Lv~Lv~Lv~V{}v~nv~}_w~}m{|w~}av~|n{|v~`v~|n{|v"
4471       "~`v~|n{|v~`v~|n{|v~`v~|n{|v~W{}u~Wr~q{|v~_v~n{}v~|`v~n{}v~|`v~n{}v~|`v~n{}v~|Z{|w~y}w~}Yu~mv~|[{|w~y}w~} O{}w~}"
4472       "u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}X{}w~O{|w~y}w~} L{}w~}G{}u~|!{|}x~}|w{}~v{}w~}b{|r~|"
4473       "x{}~w{}s~|\\{|q~}Rq~|Zw~}kw~}`{|v~p{|v~]v~p{}w~}X{|q~[{}v~}     p{|v~}ly}$v}|\"{}x~}t{}x~}Yy}|s{|y~|w{|v~|_{|w~"
4474       "q{}x~}s{|w~n{|v~}l{|u~}%{}w~|Iw~} u{}w~L{}w~} tv}|P{|w~R{|v~|pv~}U{}w~}V{}v~}={}v~|Q{}w~}K{}v~|^v~|o{}v~Y{}v~U{"
4475       "}v~m{}v~P{|v~}U{|v}M{}w~}F{|}q~}6{|q~}|G{|w}|^w~ru~y|x{|}t~y|}v~|kv~|h{|v~d{}w~}m{|u~|b{|u~|i{|~}g{}w~}l{|}u~}a"
4476       "{}w~}L{}w~}Q{}u~|iv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}ot~]{}w~}V{}w~|bv~|h{}w~|n{}q~f{}u~k{}u~a{}w~}Q{}u~k{|u~c{}w~}"
4477       "kv~}c{|}h{|v~}Y{|v~|X{}v~l{}v~|[v~}v~]v~}w~n{}r~|ev~}n{}v~W{|v~|Y{}v~}F{|w~}Ew~}M{|w~|  u{}w~|o{}u~|_t~|q{|v~}_"
4478       "{|v~|P{|v~}pt~|a{|v~|Ew~}U{|v~|pt~|`w~}m{|w~}Wv~Lv~Tw~}s{}v~|Sv~_w~}mv~mv~hw~}m{|w~}a{}v~nv~}_u~}o{}v~a{}v~o{|u"
4479       "~|Zw~}@{}w~|Y{}w~|Sv~|p{|u~|Zv~{|v~[v~}x~}s{|r~_{|v~|u{}v~Uq~V{}v~|Fw~}Lw~|M{|w~| I{|y}~y}|r{|}x~}|`{}w~}qs~]u~"
4480       "n{|v~`{|w~r{|v~\\{|v~nv~}_{|w~}Mw~}m{|w~}Y{}r~X{}w~}nv~`{|v~|o{}v~|g{|v~|st~|t{|v~|g{}v~v{}w~v{|v~`{|w~}q{|w~}_"
4481       "v~|v{}w~uv~}au~}o{}v~a{|v~nv~}Vv~U{|w~}p{}w~}bv~|h{|v~`u~M{}w~}P{|u~|q{}v~}_{}g~}|b{}w~}hv~|Z{|v~Y{}u~k{}u~a{|y"
4482       "~A{}w~A{}~|Zl~|Z{}~|k{}~}[{|l~} yv~}Uv~}uy~}S{|v~S{}v~|x{|y}x~}|wu~X{|w~}Lw~|I{|v~}*{}x~|g{|x~}&{}y~}t{|x~ T{}x"
4483       "~|g{|x~}   <{|v~|o{}v~|Ux~}w{|x~}  Ex~|t{|y~}Tw~|p{}j~}j{}x~|k{}x~}aw~|p{}j~}g{}v~}Wv~|h{|v~fv~|h{|v~fv~|h{|v~f"
4484       "v~|h{|v~fv~|h{|v~g{|v~g{|v~|ov~|m{|v~V{|u~|i{|~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}w~"
4485       "|n{}q~f{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~c{}v~|n{|u~|e{}u~|l{}u~c{}v~l{}v~|c{}v~l{}v~|c{}v~l{}v~"
4486       "|c{}v~l{}v~|Y{|v~|V{}w~}Mw~}kv~c{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|k{}w~|o{"
4487       "}s~U{|v~|P{|v~|N{|v~|N{|v~|N{|v~|Dv~Lv~Lv~Lv~Uv~}p{}v~^w~}m{|w~}a{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}W{"
4488       "}u~W{}t~|qv~}_v~|p{|u~|`v~|p{|u~|`v~|p{|u~|`v~|p{|u~|Yq~Xu~}o{}v~Yq~ M{}w~}|w{}x~}v{}v~b{}w~}|m{}v~b{}w~}|m{}v~"
4489       "b{}w~}|m{}v~b{}w~}|m{}v~W{}x~}Nq~| M{|v~F{|u~ py~|V{|}y~y}|vy~|w{|y}y~y}Y{|s~}Q{|s~|Yw~}kw~}_{}v~|s{}v~|^{|w~}p"
4490       "{|v~X{|r~}Z{}u~}     q{}v~}o{|y~}$v~}\"w~|tw~|Y{}y~}|u{|y~|x{|u~^{}x~|q{|w~s{}x~}mu~}n{|s~}&{|w~|J{|w~| u{}w~L{"
4491       "}v~ u{|v~|P{}x~}Q{}v~|r{}v~T{}w~}W{}v~}O{}|k{}v~}P{}w~}]{}|l{}u~]{|v~|q{}v~|Yv~}U{|v~}o{}v~}Q{|v~}T{}v~M{}v~C{|"
4492       "}t~}6{|t~}|D{}w~}^{}x~|s{|m~y}q~|k{|v~fv~|e{}w~}n{|u~}`{}u~|l{|}y~}g{}w~}n{|}t~}`{}w~}L{}w~}P{}u~}jv~|h{}w~}hv~"
4493       "|Y{}w~}M{}w~}W{}w~}nt~^{}w~}V{}w~|bv~|h{}w~|mq~e{}u~|n{}u~|a{}w~}P{}u~|n{}u~|c{}w~}k{|v~|d{|y~}k{|u~|Y{|v~|X{|u"
4494       "~n{|u~Z{}r~}]{}s~}n{|r~e{}v~lv~}X{|v~|Z{|u~E{|w~}E{}w~M{|w~|  u{|v~p{|t~|_s~|s{|u~]u~|P{}v~}s{|s~|`u~|k{|Ww~}T{"
4495       "}v~}s{|s~|`w~}m{|w~}Wv~Lv~Tw~}r{}v~}Tv~_w~}mv~mv~hw~}m{|w~}`v~}p{}v~|_t~|q{|v~|`v~}q{|t~|Zw~}Q{|kv~|Y{}w~}S{}v~"
4496       "pt~|Z{}w~y}w~}[{}s~|rs~}_v~}s{}w~}V{}s~}W{}v~|Ew~}Lw~|M{|w~|  r{|v~|s{}s~}^u~}ov~|_w~|s{}w~|[v~}pu~]v~|Nw~}m{|w"
4497       "~}Y{|s~}Xv~m{}w~|a{|u~p{|u~|fv~}t{}x~}x~}t{}v~ev~}w{}w~w{|v~}`{|w~}q{|w~}_{}v~|w{}w~v{}v~`t~|q{|v~|`v~}p{}v~U{}"
4498       "w~|Uv~|r{|v~b{|v~fv~|bu~|M{}w~}O{|u~}t{|u~}\\{|k~}|`{}w~}hv~|Z{|v~X{}u~|n{}u~|`{|@{}w~@{|Xn~|X{|i{|Y{|n~} xv~}U"
4499       "{|v~}vy~}S{|v~T{|v~|jv~}Y{|w~}Lw~|H{|v~|*{}x~}i{}x~}${}~}s{|y~ S{}x~}i{}x~}   ;{|u~p{|u~|Ux~}w{|x~}  Ey~|s{|~}T"
4500       "{}x~}o{}j~}k{|w~k{}x~}a{}x~}o{}j~}h{}v~}W{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{}w~}f{}w~}p{|v~l{|v~U{}u"
4501       "~|l{|}y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}w~|mq~e{}u~|n{}u~|d{}u~|n{}u~|d{}u~|n{}u"
4502       "~|d{}u~|n{}u~|d{}u~|n{}u~|d{}v~|l{|u~|et~|n{}u~|c{|u~n{|u~b{|u~n{|u~b{|u~n{|u~b{|u~n{|u~X{|v~|V{}w~}Mw~}x{|p{}v"
4503       "~c{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|k{|v~p{|q~j{|gu~|Pu~|k{|_u~|k{|_u~|k{|_u~|k{"
4504       "|Vv~Lv~Lv~Lv~U{|v~}r{}v~}^w~}m{|w~}`v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|W{}u~Vu~|q{}v~|_{}v~pt~|`{"
4505       "}v~pt~|`{}v~pt~|`{}v~pt~|Y{}s~}Xt~|q{|v~|Y{}s~} Lu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|W{}x~}N{}s~} "
4506       "M{|v~|Ev~} py~|Jy~|M{}t~O{|u~}Xw~}kw~}_{|t~}w|}u~}]{}w~}ov~|Xr~|Y{}t~}y|     tt~|r{}x~}$v~}\"w~t{|w~X{}v~}y|y{|"
4507       "y~y|}t~|_{|x~}ow~}tw~|m{|t~|r{|}q~}&w~}J{}w~ t{}w~L{}v~ u{|v~|Pw~|Pu~|t{}v~|\\s|}w~}r|a{}v~}Nx~}|p{}t~O{}w~}]{}"
4508       "y~}|q{}t~|\\{}v~|s{}u~Y{|v~|T{}u~|r{}u~|_{~}|r{|}u~|T{}v~M{}v~@{|}w~}6{|w~}|A{}w~}^{|w~r{|o~}{}s~}iv~}f{}w~}e{}"
4509       "w~}q|y}s~|_{}t~|p{|}w~}g{}w~}r|y}q~}_{}w~}g|`{}w~}O{}t~}o{|}u~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}mt~_{}w~}h|i{}w~|bv~"
4510       "|h{}w~|m{}r~dt~}|r{|t~|`{}w~}Ot~}q{|t~}b{}w~}jv~}d{|w~}|p{|}u~}X{|v~|W{}u~|q{}u~|Z{|r~|]{|s~|mr~f{|v~|l{|v~|Y{|"
4511       "v~|[{|u~}b|^{|w~}E{|w~|N{|w~|  tv~}r{}s~|_w~y}x~}|w{|}u~|]{|u~|p{|}|^t~y|x{|}w~}w~|`{|u~|n{|y~|Xw~}St~y|x{|}w~}"
4512       "w~|`w~}m{|w~}Wv~Lv~Tw~}q{}v~}Uv~_w~}mv~mv~hw~}m{|w~}`{}v~}r{}v~}^s~}s{|v~}_{}v~}s{|s~|Zw~}Qy~}|o{}w~}X{|v~}|U{|"
4513       "v~}s{|s~|Z{|q~Z{|s~qs~|`{}w~}qv~}Vs~|X{}v~|Dw~}Lw~|M{|w~|  qu~|u{}w~|v~}_s~|s{|u~^{}w~t{}w~}Z{|v~}|t{|}v~|]{}v~"
4514       "|ny|^w~}m{|w~}Xs~|Y{}w~}m{|w~}a{|t~|s{}t~}f{}v~}v{|w~{w~|v{}v~}e{}v~}x{}w~x{}u~_{|w~}q{|w~}^u~}|y{}w~x{}u~}`s~}"
4515       "s{|v~}_{|v~}|t{|}v~|U{|v~}|W{|v~|t{|v~|bu~f|v~}c{}v~}h|_{}w~}Vs}t~}v{}t~s}`{|y}t~y}|]{}w~}hv~|Z{|v~Wt~}|r{|t~|#"
4516       "{}w~ vp~| {|p~} wv~}T{}v~}wy~}v{}~Z{|v~S{}x~|hx~}X{|w~}Lw~|G{}w~}){|w~|m{|w~|\"{|}q{} R{|w~|m{|w~| XY| ${|u~}r{"
4517       "|t~}Ux~}w{|x~}  E{}qy|T{|w~c{}x~gw~|lw~}a{|w~c{}x~e{}v~}Vv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~|f"
4518       "{|v~pv~}l{|v~}h|h{}t~|p{|}w~}c{}w~}g|a{}w~}g|a{}w~}g|a{}w~}g|X{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}w~|m{}r~dt~}"
4519       "|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|d{|v~|j{|v~}f{}s~}|r{|t~|a{}u~|q{}u~|a{}u~|q{}u~|a{}u~|q{}u~"
4520       "|a{}u~|q{}u~|X{|v~|V{}w~}Mw~}xy~}y|wy|u~|bv~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|jv~}r{}w~}"
4521       "|u~|o{|}y~g{|u~|p{|}|_{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|Wv~Lv~Lv~Lv~T{}u~}|x{|}u~}]w~}m{|w~}`{}v~}"
4522       "r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}V{}u~V{|v~}r{}v~}^{|v~}s{|s~|`{|v~}s{|s~|`{|v~}s{|s~|`{|v"
4523       "~}s{|s~|Xs~|Xs~}s{|v~}Ws~| K{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~U{}x~}N{|s~| M"
4524       "{|w~|D{}w~| q{|y~}K{|y~}L{}v~|N{}v~Ww~}kw~}^{|j~}\\v~|o{}w~}X{}s~W{|^~}    -s~}v|}v~}$v~}#{|w~t{|x~}X{}e~|^w~|o"
4525       "{|w~|v{}w~k{|s~}v|}t~y}v~}'{}w~Jw~} t{}w~L{}v~ u{|v~|Q{|w~O{|u~}w|}u~}\\{|e~|ab~`u~w}|x}r~|O{}w~}]{}v~w}|x}s~}Z"
4526       "t~}w|}t~X{}w~}S{|t~y}v|}t~}^v~y}y|y}s~|S{}v~M{}v~={|}~}6{|y~}|?{}w~}]w~}q{}r~|y{}u~}h{|v~|f{|v~e{}c~|]{}s~y}v|y"
4527       "}t~}g{}b~|^{}c~}`{}w~}N{}r~y}v|y}r~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}lt~`{}d~}i{}w~|bv~|h{}w~|lr~cr~}v|}s~}_{}w~}Ns~"
4528       "y}v|}s~}a{}w~}j{|v~|e{|s~}u|}r~W{|v~|Vs~}v|y}t~|Xs~}\\{|s~|m{}t~}fv~}j{}v~Y{|v~|[{}\\~}^{|w~}Dw~}N{|w~|  t{}u~y"
4529       "|x{|}w~y}w~|_w~}{k~|[{|t~}|wy|}x~|^{|k~y|w~|_{|t~}|vy|y}w~|Xw~}S{|k~y|w~|`w~}m{|w~}Wv~Lv~Tw~}p{}v~}Vv~_w~}mv~mv"
4530       "~hw~}m{|w~}_{}u~}|x{|}u~}]w~y}w~y|yy|}u~|^{}u~}|x{|}w~}w~|Zw~}Qv~}y|v{|}u~|Wm~[{}u~}w|}v~}w~|Y{}s~}Yt~}q{}t~|a{"
4531       "}v~p{|v~|W{}t~W{}d~Uw~}Lw~|M{|w~|  q{|u~}|y{|}v~{|s~br~}y|yy|}u~|^{|w~}v{}v~X{}u~}y|{y|}u~}\\{|t~}|vy|y}y~}^w~}"
4532       "m{|w~}X{}t~Xv~|lv~|b{|s~}v|}q~x}hu~}|{y|w~}{}w~}|{|}u~c{}u~}|}w~|}t~|_{|w~}q{|v~}_{|s~}v~}s~}_w~y}w~y|yy|}u~|^{"
4533       "}u~}y|{y|}u~}S{}r~}Z{}v~}|x{|}v~}b{|Z~c{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~Vr~}v|}s~|\"{}w~ ur~| y{|r~} vv~"
4534       "}St~}y|y~}{y|}x~`{}b~}a{}~|f{~}W{|w~}Lw~|G{|w~}({|v~}|s{|}v~|  E{|v~}|s{|}v~| X{|Z~} ${|s~}y|{y|}q~}|}Xx~}w{|x~"
4535       "}   l{}x~|c{}x~h{}x~}m{|w~|`{}x~|c{}x~f{|v~}V{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~dv~|r{|"
4536       "v~k{|b~g{}s~y}v|y}t~}c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}w~|lr~cr~}v|}s~}`r~}v|}s~}`r~}v|}"
4537       "s~}`r~}v|}s~}`r~}v|}s~}b{|x~|h{|x~}f{}o~}v|}s~}_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|W{|v~|V{}w~}Mw~}xk~}"
4538       "a{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|j{}"
4539       "u~y|x{|}u~y{|t~}|vy|}v~f{|t~}|wy|}x~|^{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|Wv~Lv~Lv~Lv~S{"
4540       "}j~}\\w~}m{|w~}_{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}U{}u~V{}t~}|x{|}u~}\\{"
4541       "}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|X{}t~Ww~y}w~y|yy|}u~|W{}t~ I{}h~}\\{}h~}\\{}h~}\\{}h~"
4542       "}\\{}h~}T{}x~}Ms~ K{|y~}C{|w~ p{}x~K{}x~Kw~|L{}x~|Ww~}kw~}]{|l~}\\{|v~n{|w~}X{|s~U{}`~}    -{|h~|$v~}#{}x~}t{}x"
4543       "~}X{|}g~|^{}x~}m{}w~}y|}v~|j{|g~}y{}v~}({|w~|L{|w~| t{}w~L{}v~ u{|v~|Q{}x~}N{}k~}[{|e~|ab~`e~|N{}w~}]{}g~}Y{|i~"
4544       "|Xv~|R{|g~}]i~|R{}v~M{}v~;y|5{|<{}w~}]{|w~|p{|v}|w{|x}|e{}v~dv~}f{}e~}|[{}d~|g{}d~}\\{}c~}`{}w~}M{}c~}|g{}w~}hv"
4545       "~|Y{}w~}M{}w~}W{}w~}kt~a{}d~}i{}w~|bv~|h{}w~|l{|s~b{}f~|^{}w~}M{}f~|`{}w~}iv~}e{|c~V{|v~|Uf~}W{}t~|[s~l{}t~|g{}"
4546       "v~hv~}Z{|v~|[{}\\~}^{|w~}D{}w~N{|w~|  sj~{}w~|_w~}{|m~|Y{}i~|]{|m~|{|w~|^{|f~|Xw~}R{|m~|{}w~|`w~}m{|w~}Wv~Lv~Tw"
4547       "~}o{}v~}Wv~_w~}mv~mv~hw~}m{|w~}^h~\\w~}{k~|\\k~y|w~|Zw~}Qg~}V{|n~Zk~{}w~|Y{|s~|Y{}u~}q{|t~a{|v~|o{}v~W{|u~|W{}d"
4548       "~Uw~}Lw~|M{|w~|  p{|l~|ys~be~}\\{}v~x}u~V{}j~}Z{|h~}^w~}m{|w~}X{|u~|Y{|w~}k{}w~}b{|w~}m~|s~h{|m~xm~|b{}g~|^{|w~"
4549       "}pr~a{|f~}^w~}{k~|\\{}j~}R{|r~}Y{}l~}a{}Z~}d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~U{}f~|!{}w~ tt~| w{|t~} uv~"
4550       "}R{}i~`{}b~}`{|?{|w~}Lw~|Fw~}&{}t~w}t~}  A{}t~w}t~} V{|Z~} ${|w~}m~|s~Xx~}w{|x~}   m{|x~}b{}x~hw~lk~k{|x~}b{}x~"
4551       "fv~}U{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}w~}d{}w~}r{}w~}k{|b~f{}d~|c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}"
4552       "w~}M{}w~}M{}w~}Z{|d~}|`{}w~|l{|s~b{}f~|^{}f~|^{}f~|^{}f~|^{}f~|`{|~|f{|~}f{|w~|}f~|]f~}]f~}]f~}]f~}V{|v~|V{}w~}"
4553       "Mw~}xl~}_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|ii~w{|f~e{}i~|]{|f~|^{|f~|^{|f~|^{|f~|Wv~Lv~Lv~Lv~R{}l~"
4554       "}[w~}m{|w~}^h~Zh~Zh~Zh~Zh~){|f~Zk~{}w~|^k~{}w~|^k~{}w~|^k~{}w~|X{|u~|Ww~}{k~|V{|u~| H{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|"
4555       "j~|S{}x~}M{}u~} I{}Ax~} pw~|Lw~|L{|y~|Jy~|Vw~}kw~}[{}o~|[{}w~}mv~Wt~}T{|}b~}    +{}l~}\"v~}#w~|tw~|U{|}l~}]{|w~"
4556       "ko~|h{|j~}|w{}u~({}w~L{}w~ s{}w~Lv~| u{|v~|Qw~}M{|m~}Z{|e~|ab~`g~}|M{}w~}]{}h~|W{|k~W{}v~P{|i~|\\k~}P{}v~Mv~|  "
4557       "i{}w~}\\{}w~Jv~}d{}v~f{}g~}|X{|}h~}e{}g~}|Z{}c~}`{}w~}L{|}g~}|e{}w~}hv~|Y{}w~}M{}w~}W{}w~}jt~b{}d~}i{}w~|bv~|h{"
4558       "}w~|ks~a{|i~}\\{}w~}L{|i~}^{}w~}i{|v~|e{}f~}U{|v~|T{}i~|Ut~Z{}u~}l{|t~g{|v~|h{|v~|[{|v~|[{}\\~}^{|w~}D{|w~N{|w~"
4559       "|  s{|l~|{}w~|_w~}x{}q~}|W{|j~|[{}p~|y{|w~|]{|g~|Xw~}P{}q~}|y{}w~|`w~}m{|w~}Wv~Lv~Tw~}n{|v~}Xv~_w~}mv~mv~hw~}m{"
4560       "|w~}]{}l~}[w~}{|m~|Zm~|{|w~|Zw~}Qh~|T{|o~Z{|m~|{}w~|Xs~X{}u~|pu~}av~}m{}w~}Wu~V{}d~Uw~}Lw~|M{|w~|  o{|n~|w{}u~b"
4561       "f~}Z{}p~}T{}l~}X{|i~}^w~}m{|w~}Wu~Xv~|k{|v~b{|w~y|o~|{}t~g{|o~|x{|o~}`{}i~|]{|w~}p{}s~_{}j~}|]w~}{|m~|Z{}l~}P{|"
4562       "s~}X{}n~}`X~d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~T{|i~} {{}w~ sv~| u{|v~} tv~}Q{}j~`{}b~}#{|w~}Lw~|G{|w~}${"
4563       "}m~}  ={}m~} T{|Z~} ${|w~y|o~|{}t~Xx~}w{|x~}   mw~|b{}x~i{}x~|lk~kw~|b{}x~g{|v~Tv~}d{}v~jv~}d{}v~jv~}d{}v~jv~}d"
4564       "{}v~jv~}d{}v~k{|v~|d{|v~rv~|k{|b~e{|}h~}a{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}w~|ks~a{|i~}["
4565       "{|i~}[{|i~}[{|i~}[{|i~}/{|w~|y{|i~}Z{}i~|[{}i~|[{}i~|[{}i~|U{|v~|V{}w~}Mw~}xm~|^{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~"
4566       "|_{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~|i{|l~}u{|g~d{|j~|\\{|g~|]{|g~|]{|g~|]{|g~|Wv~Lv~Lv~Lv~Q{|}p~}|Zw~}m{|w~}]{}l~"
4567       "}X{}l~}X{}l~}X{}l~}X{}l~}){|w~}l~}Y{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|Wu~Vw~}{|m~|Tu~ E{|}p~}|V{|}p~}|V"
4568       "{|}p~}|V{|}p~}|V{|}p~}|Qw~}Lu~|  i{}y~| q{|w~}M{|w~}K{|}I{|}Uw~}kw~}Y{|y}w~y}|Yv~|m{}w~|X{}u~|Q{|}e~}    *{|}p~"
4569       "}|!v~}#w~t{|w~Py|x}y~x}y|[w~|j{}r~|e{|n~}|t{}u~){|w~|N{|w~| s{}w~Lv~ t{|v~|R{|w~|L{|}p~|Y{|e~|ab~`y|}l~}|K{}w~}"
4570       "]{|}k~|S{}o~|Vv~}N{|m~}Z{}n~}|O{}v~Mv~  h{}w~}[v~L{|v~|d{|v~|g{}k~y}y|T{|}m~}|c{}m~x}y|W{}c~}`{}w~}J{|}k~}|c{}w"
4571       "~}hv~|Y{}w~}M{}w~}W{}w~}it~c{}d~}i{}w~|bv~|h{}w~|k{|t~_{|m~}|[{}w~}J{|l~|]{}w~}h{}w~}c{|}k~}|T{|v~R{|}m~|S{}v~}"
4572       "Z{|u~|kt~gv~}f{}v~[{|v~|[{}\\~}^{|w~}Cw~|O{|w~|  q{}p~}x{}w~|_v}vy}w~y}|S{}m~}Xy}w~y}|w{|w}|[{|l~}|Vw~}N{|}w~y}"
4573       "|w{}w~|`v}lw}|Wv~Lv~Tv}m{|u}Yv}_w~}mv~mv~hw~}m{|w~}\\{|n~|Zw~}x{}q~}W{}q~}|y{|w~|Zw~}Q{|}l~}P{|y}s~X{}q~}x{}w~|"
4574       "X{}u~}X{|u~o{}v~|b{}w~}kv~}X{}w~}V{}d~Uv~Lw~|M{|w~|  n{|}q~}u{|}w~bv~{}o~}|X{|r~|R{|}p~}|U{}l~}|^w~}m{|w~}W{}w~"
4575       "}Xw}|i{|w}b{|w~|{|q~|y{|t~f{|q~|v{|q~|^{|l~}[{|w~}os~]{|}o~}|[w~}x{}q~}W{|}p~}|M{|}v~}W{|p~|`{|X~|e{}c~}_{}w~}V"
4576       "k~v{}l~|^{|v~Y{}w~}hv~|Z{|v~R{|m~}| y{}w~ rx~| s{|x~} sv~}P{|}n~}|`{}b~}#{|w~}Lw~|Ty|pv~|\"y|}u~}y|  9y|}u~}y| "
4577       "R{|Z~} ${|w~|{|q~|y{|t~Xx~}w{|x~} y}|  q{}x~}aw}j{|w~kk~l{}x~}aw}gv~}U{|v~|d{|v~|l{|v~|d{|v~|l{|v~|d{|v~|l{|v~|"
4578       "d{|v~|l{|v~|d{|v~|l{|v}bv}|t{}w~}j{|b~c{|}m~}|_{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|Z{}w~|k{"
4579       "|t~_{|m~}|X{|m~}|X{|m~}|X{|m~}|X{|m~}|.w~}v{|}n~}|X{|}m~|X{|}m~|X{|}m~|X{|}m~|S{|v~|V{}w~}Mv|wy|}u~y}|Z{}p~}x{}"
4580       "w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|g{}o~|r{|l~}|a{}m~}Y{|l~}|Y{|l~}|Y{|l~}|Y{|l~}|U"
4581       "v~Lv~Lv~Lv~O{|y}v~y}|Xw~}m{|w~}\\{|n~|V{|n~|V{|n~|V{|n~|V{|n~|(w~|{|n~|V{}q~}x{}w~|\\{}q~}x{}w~|\\{}q~}x{}w~|\\"
4582       "{}q~}x{}w~|W{}w~}Vw~}x{}q~}R{}w~} B{|t}|P{|t}|P{|t}|P{|t}|P{|t}|Nw~}  3{|~}     ;f|    '{|y}w~}y|  8{|y~|X{|x~}"
4583       "h{|}w~}|ay|y}w~y}| rw~}N{}w~ ?{|w~| D{}w~I{|y}w~y}|%b|\\{|x}u~y}|!y|y}u~y}y|O{|y}w~y}| {{y|}u~y}|Vy|y}v~}y| u{|"
4584       "w~|  B{|v~| 1{|y}u~y}|  o{|x}u~y}y| Fv~|  7y|y}v~y}| {{y|y}q~|#y|y}u~y}y| {{|y}v~y}y|   a{|w~}C{}x~}O{|w~|  oy}"
4585       "v~}|vv|!{|}t~y}|!{|y}t~y}|Sv|Av~\"v|Lv~ Rv|mv|mv|hv|lv|Z{|y}u~}|Xw~}v{|}w~y}|T{|}w~y}|w{|w~|Zv|Ny|y}u~y}| {{|y}"
4586       "w~}|uw|W{|u}|Wv}|o{|v}av|ju|Xv~| sv~Lw~|M{}w~|  ly|}v~}|Uv~yy|}v~y}|S{|y}~y}|N{|y}v~y}|Qy|y}v~x}|[v|m{|w~}W{|w~"
4587       "|#{|w~|x{|}w~}|v{|}y~y}c{|y}x~y}ry}x~y}|Z{|y}s~}y|G{}w~}|Zy|v~}|Ww~}v{|}w~y}|T{|y}v~y}| x{|y}w~}|    Ry|y}v~y}|"
4588       "   Zy| rv~}M{|y}u~}|]`| Iw~|T{|y~}|u{|u~       5{|w~|x{|}w~}|v{|}x~}Wx~}w{|x~} {}y~}  r{|y}|Kw~|L{|y}|Hv~|    E"
4589       "{|y}u~y}|      qy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|+{|y~}r{|y}v~y}|R{|y}v~y}y|S{|y}v~y}y|S{|y}v~y"
4590       "}y|S{|y}v~y}y|  oy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|d{|}v~y}|n{|y}u~y}y|\\{|}t~y}|U{|y}"
4591       "t~y}|T{|y}t~y}|T{|y}t~y}|T{|y}t~y}|Rv|Lv|Lv|Lv|!v|lv|Z{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|'{}x~|w{|y}u~"
4592       "}|S{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Vv~|Vw~}v{|}w~y}|Qv~|    Mw~|                K{|y~|  e{|w~Nw~"
4593       "| ?{}w~ Cw~}      .{}w~  @{|v~|d{}|     Kv~|   !u~|     J{|w~}C{|w~O{|w~|     9w~} Iv~   bw~}9{|w~|    X{|v~ rv"
4594       "~Lw~|M{}w~|  <v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|T{|u~y}s~|       5{|w~|Ax~}w{|x~} {"
4595       "{x~|   0{|v~    ?{}y~}         R{|}        5x~|         O{|y~}  &{|v~Uw~}D{|v~    Lw~|                K{|y~|  d"
4596       "{}x~}P{}w~ >w~| D{|w~|      .w~|  ?{|v~}g{|x~|     M{|v~    {|u~|     K{|w~}Bw~|P{|w~|     :{}w~} Iw~}   bw~}9{"
4597       "|w~|    X{}w~| r{}w~|Mw~|Mv~  ;v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|T{|l~|       4{|w~"
4598       "|Ax~}w{|x~} {{}y~}   /v~|    ?x~|                  f{|x~         M{}  %{}w~|Uw~}D{}w~|    Lw~|                K"
4599       "{|y~|  d{|w~Pw~| ?{|w~ C{}w~      .{|w~  ={|u~}|l{|u~|     N{}v~    {{|u~|     L{|q~}H{}x~}V{}q~|     :v~| Iw~}"
4600       "   bw~}9{|w~|    Xv~ q{}w~}Mw~|N{|v~  ;v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|T{|}o~}|  "
4601       "     3{|w~|Ax~}w{|x~} {{|x~|   0v~}m{}    N{|x~                  e{}y~}            Rv~Tw~}Dv~    S{}x~x{|w~|   "
4602       "             K{|y~|  c{}x~}R{}x~} >{|x~| Cw~}      .{|x~|  ;{}t~}|sy|}t~|     N{|v~}    y{|u~|     M{|q~}H{|w~V"
4603       "{}q~|     ;{}v~ I{|w~}   bw~}9{|w~|    Y{}w~} q{|v~}|Ow~|P{|}v~}  ;v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}      "
4604       "    )v~}Iy~}  gw~|Q{|y}v~y}|       1{|w~|Ax~}w{|x~} yx~|   0{}v~|p{|~}    N{|x~|                  f{|x~        "
4605       "    S{}w~}Tw~}E{}w~}    S{}x~|y{|w~                J{|y~|  bw~|Sw~| >{}y~}        K{}y~}  9{|p~x}q~}|     N{|u~"
4606       "|    x{|u~     M{|q~} y{}q~|     K{|}|p{|u~| I{}w~|   bw~}9{|w~|    Z{|v~ o{}q~}Tw~|U{|p~  :v~  S{|w~}W{|w~|#{|"
4607       "w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|        W{|w~|Aw|vx| y{|x~}   0{|u~|s{}x~}    N{|x~|                "
4608       "  f{|x~|            U{|v~Sw~}F{|v~    R{|x~}y{}w~                J{|y~|  b{|x}|T{|x}|             w{}g~}|     Q"
4609       "x|y}u~}    v{|u~     N{|p} yp}|     K{|x~}y|wy|}u~} J{|}v~   aw~}9{|w~|    \\{|}v~} nq~}Tw~|U{|q~|  :v~  S{|w~}"
4610       "W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy~}  gw~|        W{|w~| :{|}w|}w~|   /t~y}x|y}v~}    U{|}|x{|w~|    "
4611       "              f{}x~|            W{|}v~}Sw~}H{|}v~}    Qq~|                J{|y}               *{|}l~}|     O{}q"
4612       "~    tt|            `{|i~} Lr~|   aw~}9{|w~|    `{}q~ l{}s~}Tw~|U{|s~}|  9v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~"
4613       "}          )v~}Iy~}  gw~|        W{|w~| :{|q~   .{|i~}    U{|q~                  ly}w|}w~|            [{}q~Rw~}"
4614       "L{}q~    P{}r~                                M{|y}u~y}y|     L{}r~|                R{|j~} Ks~}   `w~}9{|w~|   "
4615       " `{}r~| jy|v}|Tw~|U{|u}|  6v~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}          )v~}Iy}|  gw~|        W{|w~| :{|r~| "
4616       "  -{|k~}|    U{|r~}                  l{}r~}            Z{}r~|Rw~}L{}r~|    O{}t~                               "
4617       "       k{}t~}           -{|`}|    `{|}m~}| Jt~}   _w~}9{|w~|    `{}s~| :w~|   cv~  S{|w~}W{|w~|#{|w~| j{}w~ s{}"
4618       "w~Uw~}          )v~}           d{|w~| 9y}w~y}   ){}o~}|    S{|}u~}|                  k{}r~            Y{}s~|Qw~"
4619       "}L{}s~|    M{}w~}                                      j{}w~}|           +{}`~}    ]{|x}v~y}| Gw~y}   ]w~}9{|w~"
4620       "|    `{}v~}| 8w~|   cv~  S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~}                      g{|w~|     8{|}v~y}|    Ly|   "
4621       "               g{|y}w~}|            X{}v~}|Ow~}L{}v~}|    Iy|                                                  "
4622       "l{}`~}                Ww~|                                                                                     "
4623       "                                            L{}`~}                Ww}|                                         "
4624       "                                  r{" };
4625 
4626     // Define a 104x128 binary font (huge sans).
4627     static const char *const data_font_huge[] = {
4628       "                                                                                                               "
4629       "                                                                                                               "
4630       "                                                                                                               "
4631       "                                                                                                               "
4632       "                                                                                                               "
4633       "                                                                                                               "
4634       "                                                                                                               "
4635       "                                                                                                               "
4636       "                                                                                                        FY  AY "
4637       "'Z      ;W      @Y  @Y 'Z    Y  @Y (Z        :Y  ?Y (Z          0Y  ?Y (Z    >X                                "
4638       "                                                                                                               "
4639       "                                                                                                               "
4640       "                                                                                                               "
4641       "                                                                                                               "
4642       "                         )X  AX '\\ )XAV 7YDY -]      BY  BY '[ +YEY 2X  AY (\\ -YDY   'XAU 3Y  AY (\\ )XAV 8YD"
4643       "Y      LY  AY (\\ ,YEY #Y                                                                                      "
4644       "                                                                                                               "
4645       "                                                                                                               "
4646       "                                                                                                               "
4647       "                                                                                  (X  CX '^ +[CU 6ZEY .`      C"
4648       "X  CY '] -ZEZ 2X  CY (^ .ZEZ   )[CU 2Y  CY (] *[CU 7ZEZ      LY  CY (] -ZEZ %Y                                 "
4649       "                                                                                                               "
4650       "                                                                                                               "
4651       "                                                                                                               "
4652       "                                                                                                               "
4653       "                        'Y  EY '^ ,^FV 6ZEY /b      CX  DX '_ .ZEZ 2Y  DX '_ /ZEZ   +_FV 1X  CX (_ ,^FV 7ZEZ   "
4654       "   KX  CX (_ .ZEZ &Y                                                                                           "
4655       "                                                                                                               "
4656       "                                                                                                               "
4657       "                                                                                                               "
4658       "                                                                             %Y  GY '` .aHV 6ZEY 1e      DY  FX"
4659       " 'a /ZEZ 1Y  FX '` /ZEZ   +aHV 0X  EX '` .aHV 7ZEZ      JX  EX (a /ZEZ &X                                      "
4660       "                                                                                                               "
4661       "                                                                                                               "
4662       "                                                                                                               "
4663       "                                                                                                               "
4664       "                   #X  GX 'XNX 0dKW 6ZEY 1f      DY  HX &WMX 0ZEZ 0X  GX 'XMW 0ZEZ   ,dLX /X  GX 'WMX 0dLX 7ZEZ"
4665       "      IX  GX 'WMX 0ZEZ 'X                 :T                                                                   "
4666       "                                                                                                               "
4667       "                                                                                                               "
4668       "                                                                                                               "
4669       "                                                                                    ;X  IX 'XLX 1o 5ZEY 2ZLY   "
4670       "   CX  IX &WKW 0ZEZ /X  HX (XLX 1ZEZ   ,o .Y  HX (WKX 1o 6ZEZ      IY  IY (WKW 0ZEZ (X                 <Z      "
4671       "                                                                                                               "
4672       "                                                                                                               "
4673       "                                                                                                               "
4674       "                                                                                                               "
4675       "                                  =X  KX 'XJX 3WKd 5ZEY 3XGX      CX  JX 'WIW 1ZEZ .X  JX (XJX 2ZEZ   -WKd -X  "
4676       "IX (WIW 2WKd 6ZEZ      HX  IX (WIW 1ZEZ )X                 =^                                                  "
4677       "                                                                                                               "
4678       "                                                                                                               "
4679       "                                                                                                               "
4680       "                                                                                                     >X  MX &WH"
4681       "W 3VHa 4ZEY 3WDW      CX  LX 'WGW 2ZEZ -X  LX 'WHW 2ZEZ   -VHa +X  KX (XHW 3VHa 5ZEZ      GX  KX (WGW 2ZEZ )X  "
4682       "               ?b                                                                                              "
4683       "                                                                                                               "
4684       "                                                                                                               "
4685       "                                                                                                               "
4686       "                                                         ?W  MW &WFW 4VF^ 3ZEY 4WBV      BW  MX 'WEW 3ZEZ ,W  M"
4687       "X 'WFW 3ZEZ   -VF^ )X  MX 'WFW 4VF^ 4ZEZ      FX  MX 'WFW 3ZEZ *X                 ?d                           "
4688       "                                                                                                               "
4689       "                                                                                                               "
4690       "                                                                                                               "
4691       "                                                                                                               "
4692       "             ?W  X 'WDW 5UC[ 2ZEY 4VAV      AW  X &WDW 4ZEZ +W  NW 'WDW 4ZEZ   -UC[ 'W  MW 'WDW 5UC[ 3ZEZ      "
4693       "EW  MW 'WDW 4ZEZ +X                 ?f                                                                         "
4694       "                                                                                                               "
4695       "                                                                                                               "
4696       "                                                                                                               "
4697       "                                                                              @X \"X 'WBW 6UAW 0ZEY 4V@V      B"
4698       "X !W &WBV 4ZEZ +X !W 'WBW 5ZEZ   .VAW $W  W 'WBW 6UAW 1ZEZ      DW  W 'WBV 4ZEZ +W                 >f          "
4699       "                                                                                                               "
4700       "                                                                                                               "
4701       "                                                                                                               "
4702       "                                                                                                               "
4703       "                              ?X #W 'W@W      U?V      AX #W &W@V    NX #W &V@W        9W \"W 'W@V          .W "
4704       "\"W 'W@V   !W                 >XHX                                                                             "
4705       "         3Y                                                                                                    "
4706       "                                                                                                               "
4707       "                                                                                                               "
4708       "                                                                           6W $W &V>V      U?V      @W $W &W>V "
4709       "   NW $X 'V>V        8W $X (W>V          /X $W 'W>V   #W                 >XFX                                  "
4710       "                                                    5Z                                                         "
4711       "                                                                                                               "
4712       "                ,Z                                                                                             "
4713       "                                                                                             GZ                "
4714       "                    #U?V                                                           NY  7Z ,X      CVCW      MY "
4715       " 7Z ,X   $Z  7Z ,X        >Z  6Y ,X          4Z  7Y +W    7Y                                 @Z                "
4716       "                                                                                                               "
4717       "                                                         +Z                                                    "
4718       "                                                                                                               "
4719       "                       HY                                    \"U?V                                             "
4720       "              MY  8Y ,Y      CVBV      LY  9Z ,Y   #Z  9Z ,Z        >Z  8Y ,Y          3Y  8Z ,Y    9Y         "
4721       "                        ?Z                                                                                     "
4722       "                                                                                                   *Y          "
4723       "                                                                                                               "
4724       "                                                                 IY                                    !U?V    "
4725       "                                                       LY  :Y ,[ $R>U   ,V@V      MZ  :Y +Z   #Y  9Y +Z      ?R"
4726       ">U 8Y  9Y +Z %S?U        HY  :Z ,[    ;Y                                 ?[                                    "
4727       "                                                                                                               "
4728       "                                     )Y                                                                        "
4729       "                                    8U                                                                         "
4730       "    9Y                                     V@U                                                           JY  <Y"
4731       " +[ 'XAU   ,V@V      LY  ;Y +\\   #Y  ;Y +\\      CXAU 7Y  ;Z ,\\ )XAV        HY  ;Y +[    <Z                  "
4732       "               ?U                                                    ;T        $W /W                           "
4733       "                                                                                   8e   !f      LY    Y     LX "
4734       "   L]   :Y  <Y  NX 0X  >Y                                 @Y /X 0Y           K`            .X                  "
4735       "  ^                                                  =ZEY                          @Y                          "
4736       "           NVAV                                          <P              -X +Y  =Y +] )[CU 7YDY 4V@V      KY  ="
4737       "Y +] ,YDY 5Y  =Y *] .YDY 5[  M[CU 6Y  <Y ,] *[CV 7YDY      Y  =Y +] ,YEZ !Y =Y  FYDY                 8X        "
4738       "   EU                                                    :T        %W .X                                       "
4739       "                                                                       9e   !f      KY   !Y     LY   \"a   :Y  "
4740       "<Y  NX 0X  >Y                                 E^ /X 0_          %f            1]                   'c          "
4741       "                                        @ZEZ                          AY                                     MV"
4742       "CW                                          <R              4a .Y  >X *^ +]DU 7ZEZ 5U>U      JY  ?Y *^ -YEZ 4Y "
4743       " ?Y *^ .ZEZ 5[  ]DU 5Y  >Y +^ ,]DU 6ZEZ      Y  ?Y +_ .ZEZ \"Y <Y  FYEZ                 :[           FU        "
4744       "                                 7Y          -T 7W#W <Y    9X -W  DU             KY    HZ \"\\      4Z    M[ \""
4745       "Y             LZ        +\\        8]                 >Z    G[    G\\                 @e   !f      JX   !Y     "
4746       "LY   %d   :Y  <Y  NX 0X  >Y                                 Ha /X 0b          *j    L]        D_               "
4747       "    +g                 A[                      LY        8Z -ZEZ   \"Y          1o )V    FX  NZ  FY            "
4748       "%Y    ,X  NX*Z NW              3WEW    H\\                       #[ !Z \"[ \"[ \"[    G[7T              8g 0Y  "
4749       "@Y +_ ,_FV 7ZEZ 5U>U      IY  @Y +` .YEZ 3X  ?X *` /ZEZ 4[:P 8_FV 4X  ?Y +` ._EU 6ZEZ      NX  @Y *_ .ZEZ #Y ;Y"
4750       "  FYEZ                 ;]           GU                                         <b          1T :]'X @b    >W ,X "
4751       " FV             a   \"d -g      >d   (d +b            %b        4f        Bg                 Ie   \"e   \"h    "
4752       "             Ge   !f      IX   \"Y     LY   &e   :Y  <Y  NX 0X  >Y                                 Jc /X 0c    "
4753       "      -n   $g        I`                   .j        >a        ;e    HU        .U        +b        Ac 2ZEZ   'b "
4754       "         5o -]    Na (c  KY          .Y #_   8Y!W'Y\"X.c$X              3XGX    Mf                       -e +d "
4755       ",e ,e ,e   \"e=V              ;k 1Y  BY +XNW .aGV 7ZEZ 5V@V      HX  AY +XNW .YEZ 3Y  AY *WNW /ZEZ 4\\>T 9`GV 3"
4756       "X  AY +XNW .`GV 6ZEZ      NY  AX *XNW /ZEZ $Y :Y  FYEZ                 <_           IU   (Q  LZ 4Z2Z 1Q        "
4757       "                      &g   %Z +XCX    MT <a)W Ah $X  HX +X  GV           GX 3e )_ /j 4n  L] ?y /i C~S =i 0g    "
4758       "        +g    L\\ 8t (m Ks 2~R E} <o HZ(Z :Z \"Z 4Z,] LZ 2_'_(^-Z Ck :q 0k ?q *n J~d'Z(Z*Z LZ=Z.\\.Z7Z(Z([$Z'~^"
4759       " @e 3X  Ff )\\    MY   #Y     LY   (g   :Y  <Y  NX 0X  >Y                                 Kd /X 0e          0p "
4760       "  (m        Lb                   1m ,\\ 5~S E~R Ah 'Z :~]+[;Z;Z Ik    LW    DX    DW        /i   ?Y(Y   4h 5ZEZ"
4761       " ,\\ ,h        7\\ -o .`   $f -h  NY    No     %_ %c   @_\"X-_\"W0h&W   .\\ $\\ \"\\ #\\ #\\ )g 5~a Lm D~S I~S "
4762       "H~R H~R 6Z !Z !Z \"Z :r 8^,Y Bk 2k 2k 2k 2k   (kAX+Z(Z#Z(Z$Z(Z$Y'Y&[%[ MZ  Im 1X  CY *WMX /bHV 7ZEZ 5V@V      G"
4763       "X  CY *WLW /YEZ 2Y  CY *WLW 0ZEZ 3[AW :bHV 3Y  BX *WLW 0bHV 6ZEZ      MY  CX *XMX 0ZEZ $X 9Y  FYEZ             "
4764       "    =a M~i        7U   (Q  N_ 9_8_ 3R                              )k   'Z +XCX +X@X 4T >e,X Cl &X  IX *X  GV  "
4765       "         GX 5i 0d 2p ;u !^ ?y 2o F~S @n 4j            /l    N\\ 8x .r Nx 7~R E} >t KZ(Z :Z \"Z 4Z-] KZ 2_'_(^-Z"
4766       " Ep =t 5o Au 1u N~d'Z(Z)Z MZ<Z/\\/Z5Z*['[&Z&~^ @e 3X  Ff )]    MY   $Y     LY   )h   :Y  <Y  NX 0X  >Y         "
4767       "                        Le /X 0e          1r   +r        c                   3o -\\ 5~S E~R Dn *Z :~]+[;Z;Z Ko "
4768       "   Y    EX    EY        2m   @Y)Y   6l 7ZEZ 0e 2k        >e 1o 0c   'j /i  X   !r     (b 'g   Eb\"W0c#X0i(W   -"
4769       "\\ $] #\\ $] #\\ (f 6~b r F~S I~S H~R H~R 6Z !Z !Z \"Z :w =^,Y Ep 6p 7p 7o 7p   ,oDY+Z(Z#Z(Z$Z(Z$Y'Y%Z%Z LZ  Kp"
4770       " 1X  DX *WKW /WMYJV 6ZEZ 5V@V      GY  EY *WKX 0YEZ 1Y  EY *XKW 1ZEZ 2[EZ :WMZKV 1Y  DX *WKX 1WLYKW 6ZEZ      L"
4771       "Y  EY *WKW 0ZEZ %X 8Y  FYEZ                 >c M~h        7T   (S !a <b:b 6S          %|                  $o   "
4772       ")Z +XCX +W?W 3T ?g.X Dp (X  IX )X  HV           HY 6l 7i 5t <v #_ ?y 3p F~S Aq 8n            3p (Y $^ 9z 2v!{ :"
4773       "~R E} Az NZ(Z :Z \"Z 4Z.] JZ 2`)`(_.Z Gt ?w :s Cx 5x!~d'Z(Z)Z N[<Z/\\/Z5[,[%Z'[&~^ @e 2X  Gf *_    MX   $Y     "
4774       "LY   )h   :Y  <Y  NX 0X  >Y                 >X               8f /X 0f          3t   -s        c                "
4775       "   4q /^ 6~S E~R Fr ,Z :~]+[;Z;Z Ms   #[    FX    F[        4n   @Y*Y   6m 7ZEZ 3k 5l        Bk 4o 1f   )k 0k #"
4776       "X   #u     (b (i   Fb#X0c#W/k+X   .^ %] $^ %] $^ (d 5~b\"v H~S I~S H~R H~R 6Z !Z !Z \"Z :{ A_-Y Gt :t ;t ;s ;t "
4777       "  0sGY*Z(Z#Z(Z$Z(Z$Y'Y$Z'[ LZ  Ls 2X  FX *WIW 1WJc 6ZEZ 4VBV      EY  FX *XJW 0YEZ 0X  EX )WJW 1ZEZ 1[I^ <WJc 0"
4778       "X  EX )WJW 2WJZNW 5ZEZ      KX  FY *WIW 1ZEZ &X 7Y  FYEZ                 ?d M~h        8U   )T #e ?d=e 8U      "
4779       "    *~Q                  &r   *Z +XCX +W?W 3T @i/W Dq (X  JX (X  HV           HX 7o <m 7x >x %_ ?y 5r F~S Ct :p"
4780       "            6s /e *^ 9| 6z#~ =~R E} B}!Z(Z :Z \"Z 4Z/\\ HZ 2`)`(_.Z Iw @y >w Ez 9z!~d'Z(Z)[ Z;Z0]/Z4Z,Z$[(Z%~^ "
4781       "@e 2X  Gf +a    MX   %Y     LY   *i   :Y  <Y  NX 0X  >Y                 >Y               9f /X 0g          5v  "
4782       " 0u        d                   6_K_ 0^ 6~S E~R Gu .Z :~]+[;Z;Z w   &]    GX    G]      6U &o   ?Y+Y 7X )n 7ZEZ "
4783       "6p 7m        Eo 6o 2h   *l 1l %X   #v     (b )k   Gb$X/c$X/l,W   -^ &_ %^ &_ %^ 'b 4~b$z J~S I~S H~R H~R 6Z !Z "
4784       "!Z \"Z :~ D_-Y Hw =v >w >w >w   4wIX)Z(Z#Z(Z$Z(Z$Y'Y$[)[ KZ  Mt 1X  HX )WHW 2VHb 6ZEZ 4WDW      DX  GX )WHW 1YE"
4785       "Z /X  GX )WHW 2ZEZ 0[M` ;VHb /X  GY *WHW 3VHb 5ZEZ      JX  GX )WHW 2ZEZ 'Y 7Y  FYEZ                 ?e M~f    "
4786       "    7U   )U %g Bh@g :W          .~T                  't   +Z +XCX ,X@X 3T Ak1X Er (X  JX 'X  IV           HX 8q"
4787       " =m 7y ?y '` ?y 6s F~S Dv <r            8u 4m /_ 9~ :~%~Q ?~R E} D~Q\"Z(Z :Z \"Z 4Z0\\ GZ 2`*a(`/Z Jz Bz Az F{ "
4788       ";{!~d'Z(Z(Z Z;Z0^0Z3Z.[#[*Z$~^ @X %X  :Y ,c    MX   &Y     LY   +^   .Y  <Y  NX 0X  >Y                 >Y      "
4789       "         :] %X &]          5]C\\   1v        Nc                   7\\D\\ 1_ 6~S E~R Iy 0Z :~]+[;Z;Z!y   (_    H"
4790       "X    H_      7U 'p   ?Y,Y 6X *o 7ZEZ 8t 9YH]        Ht 9o 3i   *XG[ 1VE[ &Y   %x     (b *[I[   Hb$W.c%X.VE[-X  "
4791       " ._ &_ %_ '_ %_ '` 4~c%} L~S I~S H~R H~R 6Z !Z !Z \"Z :~Q F`.Y Jz @z Az Ay Az   7zKX(Z(Z#Z(Z$Z(Z$Y'Y#[*Z JZ  Na"
4792       "J_ 2X  IX )WGW 2VG` 5ZEZ 4XFX      CX  IX )WFW 2YEZ .X  IX )WFW 3ZEZ /j 8VG` -X  HX *WFW 4VG` 4ZEZ      IX  IX "
4793       ")WGW 2ZEZ 'X 6Y  FYEZ                 ?XKX M~f        7T   )W 'i DiAi ;X          1~V                  (w   -Z "
4794       "+XCX ,X@X 3T AZI[2W Es (X  KX &X  IV           HX 9s >m 7z @z )a ?y 7t F~R Dx >t            9v 8s 2` :~P <~Q&~S"
4795       " A~R E} E~T$Z(Z :Z \"Z 4Z2] FZ 2a+a(`/Z K| C{ C} H| =|!~d'Z(Z(Z!Z9Z1^1Z2[0[!Z+[$~^ @X $X  ;Y -e    MX   'Y     "
4796       "LY   +[   +Y  <Y  NX 0X  >Y                 >Y               :[ #X #Z          6\\?[   2v        F\\           "
4797       "        8Z@[ 2` 7~S E~R J{ 1Z :~]+[;Z;Z#}   +`    HX    Ia      8U (q   >Y-Y 6X +p 7ZEZ 9bMb ;U@Y        JbMb :"
4798       "n 3ZIZ   +T@Y 2R>Y 'X   %y     (XLV +ZEZ   IXMW%X.YMW%W-R>Y.W   -` '_ &` '_ &` '` 4~c'~R N~S I~S H~R H~R 6Z !Z "
4799       "!Z \"Z :~S Ha/Y K| B| C| D} D|   9|MX'Z(Z#Z(Z$Z(Z$Y'Y\"Z+[ JZ  N]B\\ 2X  JX *WEW 3UE_ 5ZEZ 3YJY      AX  JW )WE"
4800       "W 2YEZ -X  KX (WFW 3ZEZ .f 5UE_ ,X  JX )WFW 4VF_ 4ZEZ      HX  KX )WEW 3ZEZ (X 5Y  FYEZ                 @YJW M~"
4801       "e        7U   *X (j EkCk =Y          3~X                  )x   -Z +XCX ,W?X 3T BYEY3X Ft (X  KX %X  JV         "
4802       "  IX 9u ?m 7{ A{ *a ?y 8u F~R Ez @v            :v :w 4` :~Q >~S'~U C~R E} G~V$Z(Z :Z \"Z 4Z3] EZ 2a+a(a0Z M~P D"
4803       "| E~P I} ?}!~d'Z(Z'Z\"Z9Z1^1Z1Z0Z [,Z#~^ @X $X  ;Y .g    MW   'Y     LY   +Y   )Y  <Y  NX 0X  >Y               "
4804       "  >Y               :Z \"X \"Z          7[=Z   3aE[        E[                   9Z>[ 3` 7~S E~R L~ 2Z :~]+[;Z;Z$"
4805       "~P   -b    IX    Jc      9U )r   >Y.Y 5X ,]DX 7ZEZ ;\\>\\ <R;X        M]>\\   0XDX   ,R=Y  MX (X   %hEW     (SG"
4806       "V ,YAY   JSHW%W-SGW&X GX/W   ,` (a '` (a '` (a 5~d(~S N~S I~S H~R H~R 6Z !Z !Z \"Z :~T Ia/Y L~P F~P F~P F~P F~P"
4807       "   <~X&Z(Z#Z(Z$Z(Z$Y'Y\"[-[ IZ  \\>Z 1X  LX )VCW 4UD] 4ZEZ 2f      ?X  LX )WDW 3YEZ ,W  KX )WDW 4ZEZ -b 2UD] *W"
4808       "  KX )WDW 5UD] 3ZEZ      GW  LX (VCW 4ZEZ )X 4Y  FYEZ                 @XIX M~d        7U   *Y *l GmDl ?[       "
4809       "   6~Z                  *`C\\   -Z +XCX ,W?W 2T CYCY5X E]CZ (X  LX $X  JV           IX 9]E^ @m 7aGb B^Ec ,b ?y "
4810       "9aF[ F~R E_C_ B_E^            ;]E_ ={ 7b ;~R @cBb'~V D~R E} HeBc$Z(Z :Z \"Z 4Z4] DZ 2b-b(a0Z NbCb E} GbCb J~ Aa"
4811       "B_!~d'Z(Z'Z#[9Z2_1Z0Z2[ N[.Z\"~^ @X $X  ;Y /i    MW   (Y     LY   ,Y   (Y  <Y  NX 0X  >Y                 >Y    "
4812       "           :Y !X !Y          8[;Z 1\\ 0\\:U        D[                   ;Z<Z 4b 8~S E~R M~R 4Z :~]+[;Z;Z%bCb   "
4813       "/d    JX    Ke      :U )]BW   =Y/Y 5X ,[?U   3Z8[ &W        NZ7Z   2XBW    EX  LW )X   %iEW      KV -Y?Y   @W&X"
4814       "!W&W EW0X   -b )a (b )a 'a )a 5~d)dCb N~S I~S H~R H~R 6Z !Z !Z \"Z :~V Kb0Y MbCb HbCb HbCb HbCb HbCb   >bCh%Z(Z"
4815       "#Z(Z$Z(Z$Y'Y![.Z HZ  Z;Z 1X  NX )WBV 5VBZ   $e      >W  MX )WBW   !X  MX )WBW   #` /UBZ (W  MX )WBW 6UBZ       "
4816       " 9X  MW (WCW    MX 3Y                    GXHW M~d        8U   *[ +m HnFn A]          9~\\                  +^=Y"
4817       "   -Z +XCX -X@X 2U DXAX5W E\\=V (X  LX #X .R@V?Q          ,X :\\A\\ @m 7\\>_ CY<_ -c ?y :^=V F~Q E]>^ D]@]     "
4818       "       <Z@^ @~P 9b ;Z=d Aa;^'Z>j E~R E| Ha8^$Z(Z :Z \"Z 4Z5] CZ 2b-b(b1Z `<_ FZ@d I`=` K[@d C_:Z ~b&Z(Z'Z#Z8Z2`"
4819       "2Z0[4[ LZ/[\"~^ @X #X  <Y 0\\N]    NX   )Y     LY   ,Y   (Y  ;X  NX 0X  >Y                 >Y               ;Z "
4820       "!X !Y          8Z9Y 6d 4[5R        CZ                   ;Y:Z 5b 8~R D~Q MbAb 8` =~]+[;Z;Z&`=`   1f    KX    Lg "
4821       "     ;U *\\=T   =Y0Y 4X ,Z;R   5Z3Y &W       !Y3Y   3W@W    EW  LX *W   %jEW      KV -X=X   @W'X W'X EX1W   ,b "
4822       "*b (b )b )b )b 7ZH~R)a:] N~R H~R G~R H~R 6Z !Z !Z \"Z :Z>j Lb0Y N_<` J`<_ J`=` J`=` J`=`   @`=e%Z(Z#Z(Z$Z(Z$Y'Y"
4823       " Z/[ HZ !Z9Y 0W  X )WAW 6VAW   \"d      <W  X (VAW    X  X (V@V   &a .VAW &X  NW (V@V 6UAW        6X  X )WAW   "
4824       " NW 2Y         N\\ #[ \"\\ #\\ #[  MXHW L~b        7U   +\\ ,n IoGp C_          ;~]                  ,]:X   -Z "
4825       "+XCX -X@X 8c LX@X7X E[:T (X  MX \"X /TAVAT          .X :\\?\\ Am 7Y9] CT4] .c ?Y  J]8S  Z E\\;\\ E]=[          "
4826       "  <W;\\ B~T ;b ;Z7_ C_5['Z7e GZ  MZ '`3[$Z(Z :Z \"Z 4Z6] BZ 2b-b(b1Z!_8^ GZ;` K_9_ LZ:` D]5W 3Y 9Z(Z&Z$Z7Z3`3Z."
4827       "Z4Z JZ0Z  \\ ?X #X  <Y 1\\L]    NX   *Y     LY   ,Y   (Y      8X  >Y                 >Y               ;Y  X !Y "
4828       "         8Y8Y 6f 6Z2P        BY                   <Z9Z 7c 7\\  Z (`;` >j BZ(Z+[;Z;Z'_9_   3h    LX    Mi      <"
4829       "U *[:R   <Y2Z 4X -Z8P   6Y/X 'W       #Y/Y   6W>V    EW  KW +W   %kEW      KV .X;W   @W'W NW(X CW2X   -c *c )b "
4830       "*c )c +c 7ZHZ 2_5[ NZ !Z  Z !Z  >Z !Z !Z \"Z :Z7d Mc1Y ^8_ K^8^ L_8^ L_9_ L^8_   B_9b$Z(Z#Z(Z$Z(Z$Y'Y [1[ GZ !Z"
4831       "8Y 0W !W (V?W      I`      :X !W (V?W    X \"X (W@W   *d    EX !W (W@W          0X \"X (V?W   !W 1Y        #d ,"
4832       "e +d +d ,e #XHW LZ#Z        7U   +] -o KqHp C_          <c                   2]7V   -Z +XCX -W?X <l#X?X7W E[7R "
4833       "(X  MX \"Y 0VCVCV          .X :[<[ B\\IZ 7V5] DQ0] 0XNZ ?Y  K\\4Q !Z E\\9\\ F\\;[            =U8[ DdAc =d <Z5^ "
4834       "E^1Y'Z3b HZ  MZ (_/Y$Z(Z :Z \"Z 4Z7] AZ 2c/c(c2Z!]4] HZ9^ L^5^ MZ8^ E\\0T 3Y 9Z(Z&Z%Z6Z3`3Z-Z6[ J[2Z  \\ >X #X "
4835       " <Y 2\\J]    NW   *Y     LY   ,X   'Y      8X  >Y                 >Y               ;Y  X  X          9Z7X 6g 7Y"
4836       "        #Z                   =Y8Z 7d 7[  Z )_7_ Bp EZ(Z+[;Z;Z(^5^   5j    MX    Nk      =U +[7P   <Z3Y 3X -Y   "
4837       " MX+W 'V       $X+X   7V=W    FW  KW ,W   $kEW      KV .X;X   AW(X NW(W BW2W   ,d +c *d +c *d +c 7ZHZ 3^0X NZ !"
4838       "Z  Z !Z  >Z !Z !Z \"Z :Z3a Nc1Y!^5] L]4] N^5^ N^5^ N^5]   C^5_#Z(Z#Z(Z$Z(Z$Y'Y N[2Z FZ \"Z7Y /W #W (W>V      H^"
4839       "      8X #W (W>V    NW \"W (W>W   .h    EW \"X )W>W          0W #X (V=V   \"W 0Y        &j 1i 0j 1j 1i &X <Z#Y "
4840       "       7U   +_ /p KrJr Ea          >`                   .\\5U   -Z +XCX -W?W =r'X>W8X EZ  ;X  NY !X 1XDVDX 2X  "
4841       "      &X ;[;[ BWDZ 7T2\\ \"\\ 1XMZ ?Y  L\\  2Z E[7[ G\\9[            >S5[ F`7` ?YNY <Z3\\ F]-W'Z0` IZ  MZ )^+W$"
4842       "Z(Z :Z \"Z 4Z8] @Z 2YNX/XNY(c2Z\"]2] IZ7] N]2] MZ6] G\\-R 3Y 9Z(Z&[&Z6Z4XNW3Z-[8[ HZ3[ !\\ =X #X  <Y 3\\H]    N"
4843       "W   +Y     LY   ,X   'Y      8X  >Y                 >Y               ;Y  X  Y          :Y6Y 7i 9Y        \"Y   "
4844       "                >Y6Y 7YNY 6[ !Z *^3] Dt GZ(Z+[;Z;Z)]2]   6l    NX    m      >U +Z   !Y4Z 3X -Y    NW(W (W      "
4845       " &X)X   8V<V +X  DW  LW ,W   $lEW      KV .W9W   AW(W MW)X CW2W   +YNY ,YNZ +YNY ,ZNY +YNY +YNY 9ZGZ 4^.W NZ !Z"
4846       "  Z !Z  >Z !Z !Z \"Z :Z1` d2Y\"]2] N]2] ]2]!^2]!]2]   E]2]\"Z(Z#Z(Z$Z(Z$Y'Y MZ3[ FZ \"Z6X .V $W 'V<V      GZ   "
4847       "   5W $W 'V<V    NW $W 'V<V   2m    EW #W (V<V          /W $W (W=W   #W 0Y        (n 6o 5n 5n 6n (X ;Z%Z       "
4848       " 7U   ,a 0q LrJr Fc          A_                   ,\\2S   -Z +XCX .X@X ?u(W=X:X DY  :X  NX  Y 2ZFVFZ 2X        "
4849       "'X :Z9[ CR?Z 7R/\\ \"[ 1XMZ ?Y  L[  2[ F[5Z G[7Z            >R4[ G^1^ AZNY <Z2[ G]*U'Z.^ IZ  MZ )](U$Z(Z :Z \"Z"
4850       " 4Z9] ?Z 2YNX0YNY(d3Z#]0] JZ6\\ N\\/\\ NZ5\\ G[  <Y 9Z(Z%Z&Z6Z4XNX4Z,Z8Z FZ4Z  [ <X \"X  =Y 4\\F]       #Y     "
4851       "LY   -Y   'Y      8X  >Y                 >Y               ;Y  X  Y          :Y6Y 7j :Y        \"Y              "
4852       "     >Y6Z 9YMY 5[ \"Z *]1] Hy IZ(Z+[;Z;Z)\\/\\   8n    X   !o      ?U ,[    Y5Y 2X -Y    W&W )W       'W%W   9V"
4853       "<V +X  DW  LW     )mEW      KV /X9X   BW)X MW)W BW3X   ,YMY ,YMY ,ZNZ -YMY +YNZ -YMY 9ZGZ 5]*U NZ !Z  Z !Z  >Z "
4854       "!Z !Z \"Z :Z/_!d2Y#]0]!]0]\"]0\\!\\/\\\"]0]   F\\0]#Z(Z#Z(Z$Z(Z$Y'Y M[5[ EZ \"Y5X            +P                "
4855       "       %_K[                              CY        *r 9q 8r 9r 9q *X ;Z%Z      >Q  JT   ,b 0q MsKs Ge          "
4856       "C^                   *[0R   -Z +XCX .X@X @v)X=X:W CY  :X  Y  NX 1[HVH[ 1X        'X ;Z7Z 0Z 7P,[ ![ 3XLZ ?Y  M["
4857       "  1Z EZ4[ I[5Z            ?P1Z I^-] BYLY =Z1[ H\\(T'Z-^ JZ  MZ *\\$S$Z(Z :Z \"Z 4Z:] >Z 2YMX1XMY(YNZ4Z$].\\ JZ5"
4858       "\\!\\-\\ Z4[ GZ  ;Y 9Z(Z%Z'Z4Z5XNX5Z*Z:[ F[6Z  [ ;X \"X  =Y 5\\C[       #Y     LY   -Y   'Y      8X  >Y        "
4859       "         >Y               ;Y  X  Y          :Y6Y 7k ;Y        \"Z                   @Z5Y 9YLY 5[ #Z +\\.] J| KZ"
4860       "(Z+[;Z;Z*\\-\\   :p   !X   \"q      @U ,Z    NY6Y 1X -X    W#V *W       (W#W   :U;V +X  DW  LW     )mEW      KV"
4861       " /X9X   BW*X LW*X BW3W   +YLY -YMY ,YLY -YMY ,YLY -YMZ ;ZFZ 5\\'S NZ !Z  Z !Z  >Z !Z !Z \"Z :Z-^\"e3Y#\\.]#].\\"
4862       "#\\-\\#\\-\\#\\-\\   H\\.]$Z(Z#Z(Z$Z(Z$Y'Y L[6Z DZ \"Y5Y                                    /[G[               "
4863       "               DY        +u =u <u ;u =u ,X :Y&Z      >S  LU   ,c 1q MtLt Hf          E]                   )[.Q "
4864       "  -Z +XCX .W?X Bx)X=X;X DZ  :X  X  MY 0ZIVIZ /X        'X ;Z7[ 1Z  AZ ![ 4XKZ ?Y  MZ  0Z EZ3Z I[5Z             "
4865       "Z J])\\ CYLY =Z1[ I\\%R'Z+] KZ  MZ +\\\"R$Z(Z :Z \"Z 4Z;] =Z 2YMX1XMY(YNZ4Z$\\,\\ KZ4[\"\\+[ Z4\\ I[  ;Y 9Z(Z$Z"
4866       "(Z4Z5WLW5Z*[<[ DZ7[ !\\ ;X \"X  =Y 6\\A[       $Y     LY   -Y   'Y      8X  >Y                 >Y              "
4867       " ;Y  X  Y          :Y6Y 7l <Y        !Y                   @Y4Z :YLY 4[ $Z ,\\,] M~Q MZ(Z+[;Z;Z+\\+\\   <r   \"X"
4868       "   #s      AU ,Z    MY7Y 1X -Y   \"W!V :f       (V!W   ;U;V +X  EX  MW     (mEW      KV /W7W   BW*W KW+X BW3X  "
4869       " +YLY .YKY -YLY .YKY -YLY .ZLY ;ZFZ 6\\%R NZ !Z  Z !Z  >Z !Z !Z \"Z :Z,^#YNZ3Y$\\,\\#\\,\\$\\,\\%\\+\\%\\,\\ MP"
4870       " NP N\\-]$Z(Z#Z(Z$Z(Z$Y'Y KZ7[ Dq :Z4X                                    /XC[                              EY "
4871       "       -x @x >x ?x @x -X :Z'Z      ?U  MU   -e 2q MtLt Ig          E[                   'Z,P   -Z +XCX .W?W By)"
4872       "X<W;W CZ  :X  X  MY .ZKVKZ -X        (Y <Z5Z 1Z  A[ !Z 4XKZ ?Y  N[  1Z DZ3Z IZ3Y             NY K\\%[ EYKZ >Z0Z"
4873       " J\\#Q'Z*\\ KZ  MZ +[ Q$Z(Z :Z \"Z 4Z<] <Z 2YMY3XLY(YMZ5Z%\\*\\ LZ4[\"[*\\!Z3[ IZ  :Y 9Z(Z$Z)[4Z6XLW5Z)Z<Z BZ8Z"
4874       " !\\ :X !X  >Y 7[>[       %Y     LY   -Y   'Y      8X  >Y                 >Y               ;Y  X  Y          ;Y"
4875       "5Y 7UH_ <Z        \"Z                   AY3Y ;YKZ 4[ %Z ,[*\\ N~S NZ(Z+[;Z;Z+[*\\   =\\NXM[   #X   $\\MXN\\    "
4876       "  BU ,Z  *P DY8Y 0X -Y   #W NV @k       )V NV   <V;V +X  EW  NY     )nEW      KV /W7W   BW+X KW+W CY4X   +YKZ /"
4877       "YKY .ZLZ /YKY .ZKY /YKY <ZEZ 7\\#Q NZ !Z  Z !Z  >Z !Z !Z \"Z :Z+]#YMZ4Y%\\*\\%\\*\\&\\*[%[)[%[*\\ R!R [-_%Z(Z#Z"
4878       "(Z$Z(Z$Y'Y K[9[ Ct =Y3X                                    /U@[                 \"Q            EY        .z B{ "
4879       "B{ Az B{ /X :Z'Y      >V  U   -g 4r NvNu Ji *\\ 5X.X 6\\ 7Z1Z M[                   '[    8Z +XCX /X@X C`MTL_)W;"
4880       "W<X CY  9X !Y  LX ,ZMVMZ +X        (X ;Z5Z 1Z  A[ !Z 5XJZ ?Y  NZ  0Z DY2Z J[3Z      )Q   Q   JZ M[!Z FYJY >Z0Z "
4881       "J[ 'Z)\\ LZ  MZ ,\\ \"Z(Z :Z \"Z 4Z=] ;Z 2YLX3XLY(YMZ5Z%[([ LZ3[$\\)\\\"Z3[ IZ  :Y 9Z(Z$Z)Z3Z6XLX6Z(Z>[ B[:Z !"
4882       "\\ 9X !X  >Y 8[<[       &Y     LY   -Y   'Y      8X  >Y                 >Y               ;Y  X  Y          ;Y5Y "
4883       "7RB] =\\        $Z                   BY2Y ;YJY 3[ &Z -[(\\!~U Z(Z+[;Z;Z,\\)\\   ?\\MXL[   $X   %\\LXM\\      CU"
4884       " ,Y *Q\"R DY9Y 0X -Y   #V=_?V Cm       *V LV   <U;V +X  FX \"[     (nEW      KV /W7W   BW+W JW,X F[3W   *YJY 0Z"
4885       "KZ /YJY /YKZ /YJY /YJY =ZEZ 7[!P NZ !Z  Z !Z  >Z !Z !Z \"Z :Z*]$YMZ4Y%[([%[(['\\)\\'\\)\\'\\)[!T#T\"\\-`&Z(Z#Z("
4886       "Z$Z(Z$Y'Y J[:Z Bw @Y6[                                    .Q<[                 #S            GY        /`Da E`C"
4887       "` DaD` C`Da E`C` 0X 9Y(Z      ?X !U   .h 4r NvNu Kk .c 9X.X 7^ 7Y1Y M[                   &Z    7Z +XCX /X@X C\\"
4888       "ITFY)W;W=X BY  9X !X  KY +YNVNZ *X        (X ;Z4Z 2Z  @Z !Z 6YJZ ?Y  Z  /Z DY2Z JZ1Y      ,T   T   MZ N[ NZ HZJ"
4889       "Y >Z0Z K[ &Z(\\ MZ  MZ ,[ !Z(Z :Z \"Z 4Z>] :Z 2YLX3XLY(YLZ6Z&['\\ MZ3[$['[\"Z2Z IZ  :Y 9Z(Z#Z*Z2Z7XLX7Z'[@[ @Z;"
4890       "[ ![ 8X !X  >Y 9[:[       'Y     LY   -Y   'Y      8X  >Y                 >Y               ;Y  X  Y          ;Y"
4891       "5Y %\\ =]        %Y                   BY2Z =ZJY 3\\ 'Z .\\'[#cLZLb!Z(Z+[;Z;Z,['[   @\\LXK[   %X   &\\KXL\\     "
4892       " DU -Z +S$T EY:Y /X -Z   %V?fBU Eo       +VEg=V   =V<V +X  GX *b     &nEW      KV /W7W   BW,X JW,W Nb2X   +ZJY "
4893       "0YIY /YJY 0YIY /YJZ 1YIY =ZEZ 8\\  NZ !Z  Z !Z  >Z !Z !Z \"Z :Z)\\$YLZ5Y&\\'['['\\(['['['['['[#V%V#[-a&Z(Z#Z(Z$"
4894       "Z(Z$Y'Y IZ;Z Ay BY9^                                     G[                 %U            HY        0]<^ G^=^ F"
4895       "^<] E]<^ G^=^ 1X 9Z)Z      @Z \"U   .i 5r NvNu Lm 2h ;X.X 7^ 7Y1Y N[                   &[    7Z +XCX /W?X D[GTC"
4896       "V)W;W=W AZ  :X \"Y  KY *j (X        (X <Z3Z 2Z  @Z !Z 6XIZ ?Y  Z  0Z DZ2Z JZ1Z      0W   V   Y NZ KZ IYIZ ?Z0Z "
4897       "K[ &Z(\\ MZ  MZ -[  Z(Z :Z \"Z 4Z?\\ 8Z 2YKX5XKY(YLZ6Z&[&[ MZ3[%[&\\#Z2[ JZ  :Y 9Z(Z#Z+Z1Z7WJW7Z&Z@Z >Z<Z ![ 7X"
4898       "  X  ?Y :[8[     \"\\ 3YBZ  \\ ,ZAY 4\\ &Y \"Z 0YAZ     \"X  >Y .Y3Y 3Z '\\  MZ )Z  ;Z 2^ +Y               ;Y  "
4899       "X  Y        6Y /Y5Y $[ =`  G^ !Z    IZ             M\\     #Y2Z =YIZ 3\\ (Z .[%[%aIZI`\"Z(Z+[;Z;Z-[%[   B\\KXJ["
4900       "   &X   '\\JXK\\      H\\ 1Z ,U&V EY;Y /X ,Z   'V@jDV Gp       +UDj?V   >V<V +X  GW )`     $nEW      KV /W7W   "
4901       "BW-X IW-X N`0W   *YIZ 1YIY 0YHY 1YIY 0ZIY 1YIZ ?ZDZ 8[  MZ !Z  Z !Z  >Z !Z !Z \"Z :Z(\\%YLZ5Y&[&['[&[)\\&[)[%[)"
4902       "[&[$X'X%[-b&Z(Z#Z(Z$Z(Z$Y'Y I[=[ Az CY;` 5\\ $] $\\ \"\\ #\\ $] 8\\/[ 3\\ '\\ #\\ \"[ \"[          \"[ &Z &[ !["
4903       " #\\ #[ ![    G[@W            IYBZ        J]8] I\\7\\ H]8] I]8] I\\7\\ 2X 8Y*Z      @Z \"U   .k 5q N~o Mm 4l =X"
4904       ".X 7^ 7Z3Z NZ                   %Z    6Z +XCX /W?W D[FT@S)W;W>X AZ  :X \"Y  JX (f &X        )X ;Z3Z 2Z  @Z !Z 7"
4905       "XHZ ?Y !Z  /Z CY1Y JZ1Z      2Y   Y  $Z Z HY JYHY ?Z/Y L[ %Z'\\ NZ  MZ -[  Z(Z :Z \"Z 4Z@\\ 7Z 2YKX5XKY(YKZ7Z'["
4906       "$[ NZ2Z%[%[#Z2[ JZ  :Y 9Z(Z#[,Z1Z8XJW7Z%ZB[ >[>Z !\\ 7X  X  ?Y ;[6[     (e 7YE` (e 3aEY 8c 2r 5`DX GYEa (X  NX "
4907       "0X1Z 8Y FXD`9` YD` -c 9XD` /aEX :XD] 6g 7t BX0Y LY)Y+X6Z6X)Z/Z NX)Y I} 2Y  X  Y        9_>W KY5Y #[ =c  h >XD` "
4908       "AT#X 5Y 6X0X LY'Y ?RCW ?~Y!X?X?X ;d 'r!~W KZ1Y =YHY 2\\ )Z /[$[%_GZG_#Z(Z+[;Z;Z-[%[   C\\JXI[   'X   (\\IXJ\\  "
4909       " (Y  d 5Z -W(X FY<Y .X ,[   (UAmDV Iq       ,VDl@U   >V=W +X  HX )^   ,Y1Y HnEW      KV 0X7W   BW-W HW.X M^/X )"
4910       "Y +YHY 2YHZ 1YHY 2ZHY 1YHY 2ZHY ?ZDZ 9[  LZ !Z  Z !Z  >Z !Z !Z \"Z :Z'[%YKZ6Y'\\%[)[$[*[%[)[%[)[%[%Y)Z&[.d'Z(Z#"
4911       "Z(Z$Z(Z$Y'Y H[>Z @{ DY=b ;f -f -f ,e -f -f Ae7c ;e /b )c *c *c 'Y  NX  NX  X  E[ >XD` -c )c *b *c )c '\\ &bDX L"
4912       "X0X GX0X GX0X GX0X KY)X KYE` ?Y*Y     8[4\\ K[3[ J\\4[ I[4\\ K[3[ 3X 8Z+Z      AZ !U   /m 6q N~o No 6o ?X.X 8_ "
4913       "6Y3Z Z                   $Z    6Z +XCX 0X@X DZET>Q)W;W>W ?Y  :X \"X  IY 'b $X        )X ;Z2Y 2Z  @Z !Z 8YHZ ?Y "
4914       "!Z  0[ CY1Y JZ1Z      5\\   \\  'Z!Z FY LZHZ @Z/Y L[ %Z&[ NZ  MZ .[  NZ(Z :Z \"Z 4ZA\\ 6Z 2YKX6YKY(YKZ7Z'[$[ NZ"
4915       "2Z&[#Z#Z2[ JZ  :Y 9Z(Z\"Z,Z1Z8XJX8Z%[D[ <Z?[ \"\\ 6X  X  ?Y <[4[     -l :YGd ,k 9eGY :h 5r 8eGY GYGe +Y  NX 0X3"
4916       "\\ 8Y FYGd=c!YGe 2h ;YGd 3eGX ;YG` 9m :t BY1Y LZ+Z+Y7[7Y*[1Z MY+Z J~ 2Y  X  Y        <eAW KY5Y \"Z <f 'o CYFd D"
4917       "Y(Y 5Y 6Y1Y MY'Z CUE\\ B~Y!Y@X@Y =h 0z\"~W KY0Y >ZHY 1\\ *Z /[#['^EZE^$Z(Z+[;Z;Z.[#Z   C[IXH[   (X   ([HXI[   ("
4918       "Z $k 9Z .Y*Z FY=Y .X ,\\   *UAnCU J^CW       -VCmAV   ?W>V *X  IX (a   /Y1Y HnEW      KV 0X7W   BW.X HW.W La3X "
4919       "(Y ,ZHY 2YGY 2ZHZ 3YGY 1YHZ 3YGY @ZCZ 9[  LZ !Z  Z !Z  >Z !Z !Z \"Z :Z'\\&YJY6Y'[$[)[$[*[$[+[#[+[$[&[+\\([.e'Z("
4920       "Z#Z(Z$Z(Z$Y'Y GZ?Z ?| EY>c >l 4l 3l 2l 3l 4l Gl=h @k 5h /h /h /h )Y  Y  NX  Y  E[ ?XFd 1g .h /h /h /h )\\ )hHX "
4921       "LY0X HY0X GX0X GX0Y LZ+Y KYGd AY*Y     9[EXD[ M[1[ L[1[ K[1[ M[1[ 4X 8Z+Y      A[ !T   /n 6q N~o q 8q @X.X 8` 7"
4922       "Y3Y Z                   $Z    5Z +XCX 0X@X DYDT EW;W?X ?Y  :X #Y  IY %^ \"X        )X <Z1Z 3Z  @Z !Z 8XGZ ?Y !Z"
4923       "  0Z BY2Z JY0Z      8_   _  *Z!Y DX LYFY @Z/Y M[ $Z&[ NZ  MZ .[  NZ(Z :Z \"Z 4ZB\\ 5Z 2YJX7XJY(YJZ8Z([#[ NZ2Z&["
4924       "#[$Z2[ JZ  :Y 9Z(Z\"Z-Z/Z9XJX9Z#ZDZ :Z@Z \"\\ 5X  NX  @Y =[1Z     1q <YIh 0o =hHY <l 7r 9hIY GYHg ,Y  NX 0X4\\ "
4925       "7Y FYIg@g#YHh 6l =YIh 7hHX ;YHa ;q <t BY1Y KY+Y*Y8\\8Y([3[ MY+Y I~ 2Y  X  Y        =gCX KY6Z !Z <i -q CYHh F[*Y"
4926       " 5Z 7Y1Y NZ&Y EWG` D~Y!Y@X@Y >k 5}\"~W KY0Z ?YGZ 1[ *Z /Z\"[(]CZD^%Z(Z+[;Z;Z.[#[   CYHXGY   'X   'YGXHY   'Z &o"
4927       " ;Z /[,[ FZ?Y -X +\\   +UBoBU LZ>W       -UBnAU   >W@W *X  JX 'c   1Y1Y HnEW      KV /W7W   BW.W GW/X Lc5W 'Y ,"
4928       "YFY 4ZGY 2YFY 3YGZ 3YFY 3YGZ AZCZ 9Z  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z&[&YJZ7Y'[#[*Z\"Z+[#[+[#[+[#[&[-\\'[/YM[(Z(Z#"
4929       "Z(Z$Z(Z$Y'Y G[A[ ?} FY?] :p 8q 8q 7q 8q 8p LqAl Do 9l 3l 3l 3l +Y  Y  NX  Y #i @XHh 5k 2l 3l 3k 2l +\\ +lKX KY0"
4930       "X HY0X GX0X GX0Y KY,Z KYIh CZ,Z     :ZCXC[ [/[ N[.Z MZ.[ [/[ 5X 7Y,Z      AZ !U   /o 7p M~n s :s AX.X 8` 7Z4Y Y"
4931       "                   #Z    5Z +XCX 0W?X EYCT EW;W@X >Z  ;X #Y  HX #Z  X        *X ;Z1Z 3Z  @Z !Z 9XFZ ?Y \"Z  /Z "
4932       "BY2Z KZ0[      <b   a  -[\"Y BX MYFY @Z0Z M[ $Z%[ Z  MZ .Z  MZ(Z :Z \"Z 4ZD] 4Z 2YJX7XJY(YJZ8Z([\"[ Z2Z&Z\"[$Z2"
4933       "[ JZ  :Y 9Z(Z!Z.Z/Z9WHW9Z\"ZF[ :[BZ \"\\ 4X  NX  @Y >[/Z     4t =YJj 3q >kJY >o 8r ;kJY GYJk .Y  NX 0X5\\ 6Y FY"
4934       "JiBi$YJk 8o ?YJj 9kJX ;YJc <r <t BY1Y KZ-Z)X8\\8Y'Z4[ LZ,Y I~ 2Y  X  Y        ?jDX KY6Y  Z ;k 1r CYIj G]-Z 5Z 7"
4935       "Y1Y NZ&Z HYHb E~Y!Y@X@Y @n 8~P\"~W KY0Z ?YFY 0[ +Z 0[!Z)]BZB]&Z(Z+[;Z;Z.Z\"[ LQ  GWGXFW  HQ /X /Q*Q @WFXGW   &Z"
4936       " (q ;Z .[BVB[ DY@Z -X *]   .UC^EXBU LX<W       .VBWC[AU   ?WAW )X  KX %c   2Y1Y HnEW      KV /W7W   BW/X GW/W J"
4937       "c7X 'Y ,YFY 4YFZ 3YFY 4YEY 3YFY 4ZFY AYBZ :[  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z&[&YIZ8Y([\"[+[\"[,[\"Z+Z!Z,[\"[%[/\\"
4938       "&Z/YL[(Z(Z#Z(Z$Z(Z$Y'Y F[BZ >Z@d GY@\\ :t ;t <u ;t ;t ;t tDn Gr <o 6o 6o 6o ,Y  Y  NX  Y &l @XIj 8o 5o 6n 6o 5o"
4939       " -\\ ,nLW JY0X HY0X GX0X GX0Y KY,Y JYJj CY,Y     :ZBXBZ!Z+Z Z,Z Z,Z!Z+Z 6X 7Z-Z      BZ  U   0q 7o M~n s ;u BX."
4940       "X 9a 6Y5Z!Y                   \"Z    5Z +XCX C~d&YCT EW;W@W =[  <X #Y  HY $Z  X        *X ;Z1Z 3Z  @Z !Z :YFZ ?"
4941       "Y \"Z  0Z AZ3Z KZ0[ 5Z \"[  ?e   d  0Z\"Y AY YEZ AZ0Z MZ #Z%[ Z  MZ /[  MZ(Z :Z \"Z 4ZE] 3Z 2YJY9XIY(YIZ9Z(Z![ "
4942       "Z2Z'[!Z$Z2[ JZ  :Y 9Z(Z!Z/[/Z:XHW9Z\"[H[ 8ZC[ \"[ 3X  NX  @Y ?[-Z     5v ?YKm 6r ?mKY ?q 9r <mKY GYKm /Y  NX 0X"
4943       "6[ 4Y FYKkEl%YKm ;r @YKl ;mKX ;YKd >t <t BY1Y JY-Y(Y9]9Y&Z5Z JY-Y H~ 2Y  X  Y        @lFX JY6Y  NY 9k 4s CYJl H"
4944       "^.Y 4[ 8Y1Y NY$Y J[Ie G~Y!Y@X@Y Ap ;~R\"~W KY0Z @YEZ 0[ ,Z 0Z [*\\AZA\\&Z(Z+[;Z;Z/[![ NS  GUFXEU  HS 0X 0S,S @U"
4945       "EXFU   %Z )r ;Z -[G^G[ CZAY ,X )]   /UC[>TAU NX;W )P9P     =UAWAYAU   >XDX )X  LX  HY   3Y1Y HnEW      KV /W7W "
4946       "AP9P 9W0X FW0X ?Y8W &Y -YEZ 5YEY 4ZFZ 5YEY 4ZEY 5YEY BZBZ :[  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['YIZ8Y([!Z+Z![,Z![-"
4947       "[![-[!Z$[1\\&[/XJZ(Z(Z#Z(Z$Z(Z$Y'Y EZCZ =Z;` HYA[ 8u <u =v <v =u <u!uGr Js =r 9r 9r 9r .Y  Y  NX  Y (o AXJl :q "
4948       "7q 9r 9q 7q .\\ -y IY0X HY0X GX0X GX0Y KZ-Y JYKl DY-Z     ;ZAXAZ\"Y)Y!Z*Z\"Z*Z\"Y)Y 6X 7Z-Y      BZ  NT   0s 8o"
4949       " L~m!u =w CX.X 9b 7Y5Y Y                   \"Z    5Z +XCX C~d&YCT EX<WAX <Z  <X #X  GY &^ \"X        *X ;Z0Y 3Z"
4950       "  @Z !Y 9XEZ ?Y \"Z  0Z AZ3Y JZ/Z 5Z \"[  Ag   g  4[\"X ?X YDY AZ0Z MZ #Z%[ Z  MZ /[  MZ(Z :Z \"Z 4ZF] 2Z 2YIX9"
4951       "XIY(YIZ9Z(Z Z Z2Z'[![%Z2[ JZ  :Y 9Z(Z!Z/Z.Z:XHX:Z!ZHZ 6ZDZ \"\\ 3X  NY  AY @Z*Z     6w @YLo 9t @oLY At :r =oLY "
4952       "GYLo 0Y  NX 0X7[ 3Y FYLmGn&YLo =t AYLo >oLX ;YLe ?u <t BY1Y JY-Y(Y9]9X%[7Z IZ.Y H~ 2Y  X  Y        AnGX JY7Z  N"
4953       "Z 9k 6t CYKn I^/Z 5\\ 8Y1Y Z$Z L\\Jg H~Y!Y@X@Y Br =~S\"~W LZ/Y @YDY /[ -Z 0Z NZ+\\@Z@\\'Z(Z*Z;Z;Z/[![ U  GSEXDS"
4954       "  HU 1X 1U.U @SDXES   $Z +t ;Z ,[JbJ[ AYBY +X (^   2UCZ9QAU NW:W *Q:Q     >VAW?XAU   ?ZHY (X  MX  EX   4Y1Y HnE"
4955       "W      KV /W7W AQ:Q :W0W EW1X <X:X &Y -YDY 6ZEZ 5YDY 6ZEZ 5YDY 5YEZ CZBZ :Z  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['YHZ"
4956       "9Y(Z Z+Z Z-[![-[![-Z [$[3\\%[0XI[)Z(Z#Z(Z$Z(Z$Y'Y E[E[ =Z9^ HYBZ 6v =v >w =w >v =v\"vIt Lt >t ;t ;t ;t /Y  Y  N"
4957       "X  Y *r BXKn <s :t ;t ;s :t /\\ /{ IY0X HY0X GX0X GX0Y JY.Z JYLo FZ.Y     :Y@X?Y$Y'Y#YIP5PIY\"Y.PIY$Y'Y 7X 6Z/Z"
4958       "      CZ  NU   1u 8m K~m\"w ?^C] CX.X 9b 7Z6Y X                   \"Z    4Z +XCX C~d&XBT EX=XAW ;[  =X $Y  GY ("
4959       "b $X        +X :Y/Z 4Z  @Z \"Z :XDZ ?Y \"Y  0[ @Y4Z JZ/Z 5Z \"[  Dj   j  8[\"X =X\"ZDY AZ0Z N[ #Z$[!Z  MZ /Z  L"
4960       "Z(Z :Z \"Z 4ZG] 1Z 2YIX:YIY(YHZ:Z)[ [!Z2Z'Z [%Z2[ J[  ;Y 9Z(Z Z0Z-Z;XHX;Z NZJ[ 6[FZ \"\\ 2X  MX  AY AZ(Z     7x"
4961       " AYMq ;u AqMY Bv ;r >qMY GYMp 0Y  NX 0X8[ 2Y FYMoIp'YMq ?v BYMp ?qMX ;YMf ?u <t BY1Y JZ/Z(Y:^:Y$[9[ HY/Z H~ 2Y "
4962       " X  Y        BpHX JY7Z  MY ;o 9u CYLp J_0Y 4\\ 8Y1Y Y#Z M]Jh I~Y!Y@X@Y Ct ?~T\"~W LZ/Y AZDY .[ .Z 1[ NZ+[?Z?['Z"
4963       "(Z*Z;Z;Z/Z NZ!W  GQDXCQ  HW 2X 2W0W @QCXDQ   #Z ,u ;Z +[MfM[ ?YCY +X '_   4UDZ'U W:W +R;R     >U@W?XAU   >j (X "
4964       " NX  CX   5Y1Y HnEW      KV /W7W AR;R ;W1X EW1W :X<X %Y .ZDY 6YCY 5YDZ 7YCY 5YDZ 7ZDY DZAZ ;[  JZ !Z  Z !Z  >Z "
4965       "!Z !Z \"Z :Z$Z'YHZ9Y)[ [-[ [.[ Z-Z NZ-Z [#[5\\$Z0XH[)Z(Z#Z(Z$Z(Z$Y'Y D[FZ <Z7] IYBY 5w >w ?x >x ?w >w#wKv Nu ?v"
4966       " =v =v =v 0Y  Y  NX  Y +s BXLp >u <v =v =u <v 0\\ 0{ HY0X HY0X GX0X GX0Y JZ/Y IYMp EY.Y     ;Y?X?Y%Y%Y$YJR7RIY$"
4967       "Y.RJY%Y%Y 8X 6Z/Y      CZ  MU   2v 8m K~m#y @[>\\ DX.X :c 7Z7Z!Y                   \"Z    4Z +XCX C~d&XBT DW=XB"
4968       "X :[  >X $Y  FY +f &X        +X ;Z/Z 4Z  AZ !Z ;YDZ ?YFP -Z?Q  BZ ?Z5Z JZ/Z 5Z \"[  Gj   Ii  ;[\"X1Q,W\"YCZ BZ1"
4969       "Z MZ \"Z$[!Z  MZ /Z  LZ(Z :Z \"Z 4ZH] 0Z 2YHX;XHY(YHZ:Z)Z N[!Z2Z([ NZ%Z2Z I[  ;Y 9Z(Z Z1Z,Z;XGW;Z N[L[ 4[H[ #\\"
4970       " 1X  MX  AY BZ&Z     8^Ga AYN[H_ <cI\\ B`I[MY CaH_ <r ?`H[NY GYNr 1Y  NX 0X9[ 1Y FYNqJp'YMq @aJa CYN[H_ A`I[MX "
4971       ";YNg @`E[ <t BY1Y IY/Y&X:^:Y#Z:[ GY/Y G~ 2Y  X  Y      JW5V B`M_JX IY8Z  LY =r ;cL_ CYM^Na J`1Y 5^ 9Y1Y!Z\"Z ^K"
4972       "j J~Y!Y@X@Y D_I` A~U\"~W LY.Y AYCZ .[ /Z 1Z MZ,\\?Z?\\(Z(Z*Z;Z<[/Z NZ\"Y  ;X  ;Y 3X 3Y2Y 3X    EZ -hM[ ;Z *~Q >"
4973       "YDY *X )b   6UDY%U V9W ,S<S     >U@W>W@T   =h 'X  X  AW   5Y1Y HnEW      KV /X9X AS<S <W1W DW2X 9W<W $Y .YCZ 7Y"
4974       "CY 6YBY 7YCY 6ZCY 7YCZ EZAZ ;[  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z$Z'YGZ:Y)[ NZ-[ [.Z N[.Z NZ.[ NZ\"[7\\$[1XFZ)Z(Z#Z("
4975       "Z$Z(Z$Y'Y CZGZ ;Z6\\ IYCY 4^Ga ?^Ga @_Hb ?^Ga ?^Ga ?^Ga$^GaMaI`!bH\\ @aI` ?aI` ?aI` ?aI` 1Y  Y  NX  Y ,u CXM^Nb"
4976       " @aKa >aJa ?aJa ?aKa =`Ja 1\\ 0`Ic GY0X HY0X GX0X GX0Y IY0Z IYN[H_ FZ0Z     <Y>X>Y&X#X%YJT9TIY&Y.TJY&X#X 8X 5Y0"
4977       "Z      CZ ;P4U   1w 9l J~m#z B[;[ EX.X :d 7Y7Y X                   )~Q   #Z +XCX C~d&XBT DW=XCX 9\\  ?X $Y  FY "
4978       "-j (X        +X ;Z/Z 4Z  AZ \"Z :XCZ ?YM_ 5ZE^  IZ >Y6Z IZ0[ 5Z \"[  Jj   Ci  ?\\\"X6\\2X#YBY BZ1Z MZ \"Z$[!Z  "
4979       "MZ 0[  LZ(Z :Z \"Z 4ZI] /Z 2YHX;XHY(YGZ;Z)Z N[!Z3[([ NZ%Z2Z H[  <Y 9Z(Z NZ2Z,Z<XFW;Z MZLZ 2ZHZ #\\ 0X  MX  AY C"
4980       "Z$Z     9Y>^ BcB] >_?W C^CYNY C]A] 4Y /]Bc GYNYD^ 2Y  NX 0X;\\ 0Y FYNXC\\KYD](YNYC] A]B^ DcB] C^CYNX ;YNZDQ A\\"
4981       ";V 5Y .Y1Y IY/Y&Y;_;Y\"Z;Z FZ0Y $[ 2Y  X  Y      M];\\ F]E[JX IY9[  LY >ZKf =]=V CYNYC] K`2Z 5^ 9Y1Y!Z\"Z!^JZM^"
4982       " K~Y!Y@X@Y E]C^ CaHl\"~W LY.Z BYBY .\\ 0Z 1Z M[-[>Z>[(Z(Z*Z;Z<[0[ N[$[  <X  <[ 4X 4[4[ 4X    EZ ._KUHV ;Z )~ <Y"
4983       "EY *X *e   8UDY$T!W:X .U=T     ?U?W>W@U   =f &X !X  @W   5Y1Y HnEW      KV /X9X AT=T =W2X DW2W 8W=X $Y .YBY 8ZC"
4984       "Z 7YBY 8ZCZ 7YBY 8ZBY FZ@Z ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z$[(YGZ:Y)[ NZ-Z MZ.Z N[/[ N[/[ NZ![9\\#[2YFZ)Z(Z#Z(Z"
4985       "$Z(Z$Y'Y C[I[ ;Z5\\ JYCY 4X=^ @X=] @Y=] ?Y>^ @X=^ @X=^%X=l@\\\"_?W A]@\\ @]@\\ @^A\\ @^A\\ 1Y  Y  NX  Y -w DXNY"
4986       "C] A^C^ ?^C^ A^B] @^C^ ?^C^ 2\\ 1^C_ FY0X HY0X GX0X GX0Y IY0Y HcB] FY0Y     ;X=X=Y(Y#Y'YJV;VIX&X.VJY(Y#Y 9W 4Z1"
4987       "Z      DZ =S4U   2y 9j I~l#{ BZ9Z EX.X :d 7Z8Y!Y                   *~R   #Z +XCX C~d'YBT DX?XBW 7\\  @X $Y  FY "
4988       "/ZNVNZ *X        ,X :Z/Z 4Z  AZ #Z :XBZ ?o 9ZGc  MZ =Z8[ HY0\\ 6Z \"[  Li   >j  C\\\"X8aGVBW$ZBZ CZ2Z LZ \"Z#Z!"
4989       "Z  MZ 0[  LZ(Z :Z \"Z 4ZJ] .Z 2YHX<YHY(YFY;Z)Z MZ!Z3[([ N[&Z3[ H]  >Y 9Z(Z NZ2Z,Z<XFX<Z LZN[ 2[JZ \"[ /X  LX  B"
4990       "Y DZ\"Z     :U7\\ Ca>\\ @^:T C\\?b D\\=\\ 5Y 0\\>a Ga?\\ 2Y  NX 0X<\\ /Y Fa@\\MX@[(b@\\ B]?\\ Da?] D\\?a ;b 1Z6"
4991       "S 5Y .Y1Y IZ1Z&Y;_;X![=Z DY1Y #[ 2Y  X  Y      `>` I\\B[KX IY:\\  LY ?ZDa ?\\7R Cb?\\ F[3Y 5_ 9Y1Y\"Z Y!]IYJ] L"
4992       "~Y!Y@X@Y F\\?\\ D^Ai\"~W LY.Z CZBZ .\\ 1Z 1Z LZ.[=Z>[(Z(Z*Z;Z<[0[ N[%\\  <X  <\\ 5X 5\\4\\ 5X    EZ /^IUFT ;Z ("
4993       "| ;YFY )X +h   :TDY#U\"W:X /V?V     ?U?W>XAU   <c $X \"X  ?X   6Y1Y HnEW      KV .W9W @U>V ?W3X CW3X 8X>W #Y /Z"
4994       "BZ 9YAY 8ZBZ 9YAY 8ZBZ 9YAY FZ@Z ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z$[(YFZ;Y)Z MZ-Z MZ/[ MZ/[ N[/Z M[![;\\\"[3YE[*"
4995       "Z(Z#Z(Z$Z(Z$Y'Y B[JZ :Z4[ JYCX 3U8\\ @U8\\ AV8\\ @U7\\ AU7[ @U8\\%U8h=\\$]9T B\\=\\ B\\=\\ B\\=\\ B\\<[ 2Y  Y  "
4996       "NX  Y .x Da?\\ C]?] A]?] B\\?] B]?] A]?] 3\\ 2]?] FY0X HY0X GX0X GX0Y IZ1Y Ha?] GY1Z     <X<X<X(X!X'XJX=XJY(X.X"
4997       "JX(X!X 9W 4Z1Y     >~d W5T   2{ 9i H~k$} DZ7Z FX.X :d 7Z9Z!X                   )~R   #Z   0~d&XBT DX?XCX 6\\   "
4998       " =Y  EY 0ZMVMZ +X        ,X :Z/Z 4Z  B[ %\\ :XBZ ?q ;YHg  Z <Z:[ GZ1\\ 6Z \"[  i M~c Nj  G\\!W9eIVBX%Y@Y CZ3[ M"
4999       "[ \"Z#Z!Z  MZ 0Z  KZ(Z :Z \"Z 4ZK] -Z 2YGX=XGY(YFZ<Z*[ MZ!Z3[(Z M[&Z3[ H^  ?Y 9Z(Z NZ3Z*Z=XFX=Z Kf 0[L[ #\\ /X "
5000       " LX  BY        JS4[ C`<\\ A\\5Q D[;` E[9Z 5Y 1\\<` G`<Z 2Y  NX 0X=\\ .Y F_=[MV=[)`<[ D\\<\\ E`<[ E[;_ ;` 0Z3Q 5"
5001       "Y .Y1Y HY1Y%Y<`<Y [?[ DZ2Y $[ 1Y  X  Y     !cBc J[?YLX HY<]  JX @Y?_ @[ '`<[ EZ4Z 5` :Y1Y\"Z Z#\\GYI\\ EZ:Z IY@"
5002       "X@Y FZ;[ E]>\\ 0Z 6Y.Z CYAZ -\\ 2Z 1Z LZ.[=Z=[)Z(Z*Z;Z<Z/Z LZ&\\  ;X  ;\\ 6X 6\\2\\ 6X    EZ /\\GUCQ ;Z 'z 9YGY"
5003       " )X -ZN_   ;TDX\"U\"W;Y 0W@W     ?T>W>X@T   ;a #X #X  =W   6Y1Y GmEW      KV .X;X @W@W @W3W BW4X 6W?X #Y /Y@Y :"
5004       "ZAY 8Y@Y 9YAZ 9Y@Y 9YAZ GZ@Z ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(YFZ;Y)Z M[/[ MZ/[ MZ/Z LZ/Z M[ [=\\!Z3YD[*Z(Z#Z"
5005       "(Z$Z(Z$Y'Y AZKZ 9Z4[ JYDY 3R3[ AR3[ BS3Z @S4[ AS4[ AR3[&R3e:[&]6R C\\:[ D\\:[ D\\:[ D\\:[ 3Y  Y  NX  Y /_B] E_<"
5006       "[ C[;[ B\\<\\ C\\<\\ C[;\\ C\\<\\ 3\\ 3\\<\\ FY0X HY0X GX0X GX0Y HY2Z H`<[ FY2Y     ;X<X<X)X NX)YKZ?ZJX(X/ZKX)X"
5007       " NX ;X 3Y2Z     >~d#Z6U   3} :h G~k%~P EY5Y FX.X ;ZNY 6Y9Z!X                   *~R   \"Z   0~d&YCT CXAXBW 5]   "
5008       " >Y  EY 2ZKVKZ -X        ,X :Z/Z 4Z  BZ &] :XAZ ?s =YJk #[ ;[=[ FZ1\\ 6Z \"[ #j L~d Ki  J\\!X:hKVAW%Y@Y CZ5\\ L"
5009       "[ \"Z#Z!Z  MZ 0Z  KZ(Z :Z \"Z 4ZL] ,Z 2YGX=XGY(YEZ=Z*[ M[\"Z4['Z LZ&Z4[ F`  BY 9Z(Z MZ4Z*Z=XEW=Z Jd .ZLZ #\\ .X"
5010       "  LX  BY        JQ1[ D_:[ B\\ ([9_ F[7Z 6Y 1[:_ G^9Z 3Y  NX 0X>\\ -Y F^;b;Z)_:Z D[:\\ F_:[ G[9^ ;_ /Y  EY .Y1Y "
5011       "HY2Z$Y=a=Y NZ@[ BY3Z %[ 0Y  X  Y     \"eCd L[>YLX HY>^  IY AY=] @Z &_:Z DY4Y 5a :Y1Y\"Z Z$\\GYG\\ EY9Y IY@X@Y G"
5012       "Z9[ G\\;[ 0Y 5Y.Z DZ@Y ,\\ 3Z 1Z LZ.Z<Z=[)Z(Z*Z;Z<Z/Z LZ'\\  :X  :\\ 7X 7\\0\\ 7X    EZ 0\\FU -Z &x 8YHY (X -YK"
5013       "_   >UDX!T\"X<Y 1XAX     ?T>W>X@U   :] !X $X  <W   6Y1Y GmEW      KV .Y=X ?XAX AW4X BW4W 5W@X \"Y 0Z@Y :Y@Z 9Y@"
5014       "Y :Z@Y 9Y@Z ;Z@Y HZ?Z <[  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(YEZ<Y*[ M[/[ M[0Z LZ/Z LZ/Z M[ N[?\\ Z3XBZ*Z(Z#Z(Z$Z(Z"
5015       "$Y'Y @ZM[ 9Z3[ KYDY 3P0Z AP0Z BQ0Z AQ0Z BP0Z AP0Z&P0b7Z'\\2P CZ7Z DZ7Z DZ7Z DZ7Z 3Y  Y  NX  Y 0]<Z E^:Z D[9[ C["
5016       ":\\ E\\:[ D[9[ C[:\\ 4\\ 3[9[ GY0X HY0X GX0X GX0Y HZ3Y G_:[ GY2Y     <X;X;X*X NX)XJ[A\\JX*X/[JX*X NX ;X 3Z3Z   "
5017       "  >~d&^7U   4~ 9f E~i%~R GY4Y FX.X ;ZNZ 7Y9Y!X                   )~R   \"Z    NW?W BYCT CYBXCX 6_    ?Y  EZ 5ZI"
5018       "VIZ /X        ,X :Z.Y 4Z  C[ )_ :YAZ ?t >YKn %Z 9\\A\\ EZ1\\ 6Z \"[ &j I~d Hi  N\\ W:jLVAW&Z@Z DZ8^ KZ !Z#[\"Z "
5019       " MZ 0Z  KZ(Z :Z \"Z 4ZM] +Z 2YGY?XFY(YEZ=Z*Z L[\"Z4['Z LZ&Z4[ Fc  EY 9Z(Z MZ5Z)Z>XDW=Z Ic .[NZ #\\ -X  KX  CY  "
5020       "      )Z D^8[ D\\ '[8^ FZ5Z 7Y 2[8^ G]8Z 3Y  NX 0X?[ +Y F]9`9Y)^9Z E[8[ F^8Z GZ8^ ;^ .Y  EY .Y1Y GY3Y#Y=WNX=Y M"
5021       "ZAZ AY3Y %[ /Y  X  Y     #gEf N[<YMX HYBb  IY BY;] BZ %^8Z DY5Y 5b ;Y1Y#Z NZ$[FYF[ EY9Y IY@X@Y HZ8[ H\\9[ 1Y 5Y"
5022       ".Z DZ@Z ,\\ 4Z 2[ LZ.Z<Z<Z)Z(Z*[<Z<Z/Z LZ(\\  9X  9\\ 8X 8\\.\\ 8X    EZ 1\\EU -Z %^E] EhIg 6X .YI_   ?UEX T!W="
5023       "Z 2YBY     @U>W>W?U   7W <~d BX  ;W   6Y1Y GmEW      KV -X=X ?YBY BW4W AW5X 5W@W !Y 0Y?Z ;Y?Y :Z@Z ;Y?Y :Z?Y ;Y"
5024       "?Y HZ?Z <[  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(YEZ<Y*[ LZ/[ M[0Z LZ/Z LZ0[ LZ M[A\\ NZ4XAZ*Z(Z#Z(Z$Z(Z$Y'Y @[NZ 8Z3"
5025       "[ KYDY  AZ !Y  Y  Z !Z !Z 5`5Z([ %Z5Z FZ5Z FZ5Z FZ5Z 4Y  Y  NX  Y 1\\:[ F]8Z F[7[ E[8[ E[8[ E[8[ E[8[ 4\\ 4[9\\"
5026       " GY0X HY0X GX0X GX0Y GY4Z G^8Z GZ4Z     <X;X:W+X LX*WH[C\\IX*X0[HW+X LX <X 2Y4Z     =~d(`7T   4~Q 9e E~i%~R GY3"
5027       "Y GX.X ;YMZ 7Z;Z!X                   *~R   !Z    X@X BZDT BXCYDX 6`    ?Y  DY 7[HVH[ 1X        -X 9Z.Y 4Z  D[ 7"
5028       "m 9X@Z ?v AZLp &Z 8^H_ DZ1\\ 6Z \"[ (i F~d Ei #\\ NW;lMV@W'Y>Y D~P JZ !Z#[\"~Q Dy Z  K~] :Z \"Z 4ZN] *Z 2YFX?XF"
5029       "Y(YDZ>Z*Z L[\"Z5\\([ LZ&Z5\\ Eg  JY 9Z(Z MZ5Z)Z>XDX>Z Ib ,f $\\ ,X  KX  CY        (Y D]6Z D[ '[7^ GZ4Z 7Y 2Z6] "
5030       "G]7Z 4Y  NX 0X@[ *Y F]8^8Z*]7Z FZ6[ G]6Z I[7] ;] -X  DY .Y1Y GY3Y#Y=WNX=X L[CZ ?Y4Y &[ .X  NX  Y     $iGh Z:XNX"
5031       " GYHg  HY CY8\\ CY $]7Z DY6Y 4b ;Y1Y#Z MZ&[EYE[ FY9Y IY@X@Y HZ7[ I[7[ 2Y 5~V DY>Y +\\ 5Z 2Z KZ/[<Z<[*Z(Z)Z<Z<Z/"
5032       "ZIuIZ)\\  8X  8\\ 9X 9\\,\\ 9X    EZ 1[DU -Z $Z@[ EhJh 6X /YF_   ATDX U\"X?[ 3ZCZ     @U>W>W?U     K~d CX  ;X  "
5033       " 6Y1Y FlEW      KV -Y?Y ?ZCZ CW5X AW5W 5XAX !Y 0Y>Y <Z?Z ;Y>Y <Z?Z ;Y>Y ;Y?Z JZ>~Q3[  I~Q G~Q F~Q G~Q 5Z !Z !Z "
5034       "\"Z :Z#Z(YDZ=Y*[ LZ/Z L[0Z L[0Z LZ0[ LZ L[C\\ N[5X@Z*Z(Z#Z(Z$Z(Z$Y'Y ?e 7Z3[ KYDY  @Y  Y !Z  Y  Y  Y 4_4Y)[ %Z3"
5035       "Y GZ3Y FZ4Y FZ4Y 4Y  Y  NX  Y 1[8Z F\\7Z F[7[ EZ6[ G[6[ G[6Z EZ6[   <Z9^ HY0X HY0X GX0X GX0Y GY4Y F]6Z GY4Y    "
5036       " ;W:X:X,X LX+XG[E\\GW*W0[GX,X LX <X 2Z5Z     =~d(`8U   4~R 9c D~h%~T HX2Y GX.X <ZLY 6Y;Z!X                   *~"
5037       "R   !Z    X@X BZDT BZGZCW 6b    @Y  DY 8ZFVFZ 2X        -X 9Z.Y 4Z  DZ 7l 8X?Z ?w BZMr ([ 7s C[3] 6Z \"[ +i C~d"
5038       " Cj '\\ NW;nNV@W(Z>Y D~ IZ !Z#[\"~Q Dy![  K~] :Z \"Z 4h )Z 2YFX@YFY(YDZ>Z*Z KZ\"Z5\\([ LZ&Z6\\ Ck  Y 9Z(Z LZ6Z("
5039       "Z?XDX?Z G` *d #[ +X  KX  CY        'Y E]6[ F[ &Z5] GY2Y 7Y 3Z4\\ G\\6Z 4Y  NX 0XA[ )Y F\\7]6Y*\\5Y G[5Z G\\5Z I"
5040       "Z5\\ ;] -X  DY .Y1Y GZ5Z#Y>XMW>Y K[E[ ?Y5Y &[ .Y  NX  Y     $XIZHZIY!Z:XNX GYHf  GY DY6[ CY $\\5Y CX6Y 5c ;Y1Y#"
5041       "Z MZ&[EYDZ FY9Y IY@X@Y IZ5Z IZ5Z 2Y 5~V EZ>Y *[ 5Z 2Z KZ/[<Z<[*Z(Z)Z<Z=[0[IuIZ*\\  7X  7\\ :X :\\*\\ :X      L["
5042       "CU -Z %Z>Z EiKh 6X /XC^   BTDX U\"YA\\ 4ZCZ N~d  &U>W?X>T     K~d EY  :W   5Y1Y EkEW      KV ,YAY =ZCZ DW6X @W6"
5043       "X 5W@W   'Z>Y <Y=Y <Z>Z =Y=Y ;Y>Z =Z>Y JZ>~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z :Z#[)YDZ=Y*[ LZ/Z KZ0Z L[1[ LZ0[ L"
5044       "Z K[E\\ M[6Y@Z*Z(Z#Z(Z$Z(Z$Y'Y >d 7Z2Z KYDY  @Y  Y  Y  NY  Y !Y 4^3Z*Z $Z3Z HZ3Z HZ3Z HZ2Y 5Y  Y  NX  Y 2[6Z G"
5045       "\\6Y FZ5[ G[5Z GZ5[ GZ5[ G[5Z   =[:_ HY0X HY0X GX0X GX0Y GZ5Y F\\5Z GY5Z     <X:X:X,W JW+XF[G\\FX,X1[FX,W JW <X "
5046       "2Z5Y     <~d'UNY9U   5~T H[LaM[!~g&~V JY1X GX.X <ZLZ 7Y;Y X                    Z    3Z    W?X AZET A\\M\\CX 7d "
5047       "   BZ  DY 8XDVDX 2X        -X 9Z.Y 4Z  E[ 7j 7Y?Z ?x CZNt )Z 5p @Z3] 6Z \"[ .i @~d @i *\\ MW<^Ib@W(Y=Z E| GZ !Z"
5048       "\"Z\"~Q Dy![  K~] :Z \"Z 4f 'Z 2YEXAXEY(YCZ?Z*Z KZ\"Z6\\'[ LZ&Z8] An $Y 9Z(Z LZ7Z'Z?XDX?Z F_ *c #\\ +X  JX  DY "
5049       "       'Y E\\4Z FZ %Z4\\ HZ1Y 8Y 3Z4\\ G[4Y 4Y  NX 0XC\\ (Y F[6]6Y*[4Y GZ4[ H\\4Z JY4\\ ;\\ ,X  DY .Y1Y FY5Y!Y?"
5050       "XMX?Y JZF[ >Z6Y &[ .Y  NX  Y     %WEYJYEX#Z8a GYHe  FY DX4[ DY $\\5Y CY8Z 5d <Y1Y$Z LZ'[DYD[ GY9Y IY@X@Y IY4Z J"
5051       "[5[ 3Y 6~W EY=Z *[ 6Z 2Z KZ/Z;Z<[*Z(Z)Z<Z=Z/[IuI[,\\  6X  6\\ ;X ;\\(\\ ;X      LZBU -Z %Y<Z FjMi 6X 0X@]   CTD"
5052       "W NU!ZE^ 5ZCZ M~d  &T=W@X=T     K~d FY  :X   5Y1Y EkEW 3Z    CV +ZEZ ;ZCZ EW6W ?W7XA]\"XAX   'Y=Z =Y=Y <Y<Y =Y="
5053       "Y <Z=Y =Y=Z KY=~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z L[1[ L[1[ LZ J[G\\ L[7Y?Z*Z(Z#Z(Z$Z(Z$"
5054       "Y'Y >c 6Z2Z KYDY  ?Y  X  NX  NY  Y  Y 4\\1Y+[ %Z1Y HY1Y HY1Y HY1Y 5Y  Y  NX  Y 3[5Z G[5Z HZ3Z GZ4[ HZ4Z HZ3Z GZ"
5055       "4[   >Z9` IY0X HY0X GX0X GX0Y FY6Z F\\4Z GY6Y     ;W9X9W-X JX,WD[I\\DW,W1[DW-X JX =X 1Y6Z     <~d'RKY:U   5~U J"
5056       "~T$~g'~X KY1X GX.X <YKZ 7Z<Y W                    NZ    3Y    NW?W @\\GT @jCW 7f    CZ  DY 7VCVCV 1X        .X "
5057       "8Z.Y 4Z  F[ 6h 5X>Z ?y DgF` *Z 2k >Z4^ 6Z \"[ 1j >~d =i -[ LW=\\C_?W)Y<Y Ez EZ !Z\"Z\"~Q Dy![  K~] :Z \"Z 4e &Z"
5058       " 2YEXAXEY(YCZ?Z*Z KZ\"Z8^'[ L['Z:_ @p 'Y 9Z(Z KZ8Z'Z@XBW?Z F^ (b $\\ *X  JX  DY        &X E[2Y FZ &Z3\\ HY0Y 8Y"
5059       " 3Y2[ G[4Y 4Y  NX 0XD\\ 'Y F[5[5Y*[4Y HZ2Z H[3Z KZ3[ ;[ ,Y  DY .Y1Y FY5Y!Y?WLX?Y J[GZ <Y7Z '[ -Y  NX  Z     'WC"
5060       "YKXBV#Z8` FYHc +YCY EY4[ DY $[4Z CX8Y 5e <Y1Y$Z KZ([DYCZ GY9Y IY@X@Y IY3Z KZ3Z 3Y 6~W EY<Y )[ 7Z 2Z KZ/Z;Z;Z*Z("
5061       "Z)[=Z=Z/[IuI[-\\  5X  5\\ <X <\\&\\ <X      LZBU -Z &Y:Y FjNj 6X 0X?]   EUEX NU!s 6ZCZ L~d  &T=WAY=T     K~d GX"
5062       "  9Y   5Y1Y DjEW 3Z    CV *]M] 9ZCZ FW7X5X3W7WCc%XBX5Y   JY<Y >Z=Z =Y<Y >Z=Z =Y<Y >Z=Z LZ=~Q3Z  H~Q G~Q F~Q G~Q"
5063       " 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z KZ1[ L[1Z KZ I[I\\ K[8Y>[+Z(Z#Z(Z$Z(Z$Y'Y =a 5Z2Z KYDY  ?Y  Y  X  MX  Y  Y"
5064       " 4\\1Y+Z $Y0Y IZ1Y IZ1Y IZ0X 5Y  Y  NX  Y 3Z3Y GZ3Y HZ3Z HZ2Z IZ2Z IZ3Z GZ3Z   >Z:a IY0X HY0X GX0X GX0Y FZ7Y E["
5065       "3Z GY6Y     ;W9X9W-W HW,WC[K\\CW,W2[CW-W HW =X 1Z7Z     <~d NX:U   5~V M~X%~e&~Y LX0Y HX.X =ZJY 6Y=Z W         "
5066       "           NZ    3Y    X@X ?]IT ?hCW 7h2X   ;Y  CY 7TAVAT 1X        .X 8Z.Y 4Z  G\\ 6g 5X=Z ?X?a EeB^ +Z /f ;[5"
5067       "^     4i ;~d :i 1[ LW<Z?]?W*Z<Z Fx CZ !Z\"Z\"~Q Dy![  K~] :Z \"Z 4e &Z 2YEXBYEY(YBZ@Z*Z KZ\"Z9^&[ L['[Ad >r *Y "
5068       "9Z(Z KZ8Z'Z@XBX@Y D\\ &` $\\ )X  JX  DY        &X E[2Z HZ %Z3\\ IZ/X 8Y 4Z2[ GZ3Y 4Y  NX 0XE\\ &Y FZ4[5Y*[4Z IZ"
5069       "2Z H[2Y KY2[ ;[ +X  DY .Y1Y FZ7Z!Y?WLX?X H[IZ ;Y7Y '[ ,Y  NX  NY  *Q   NV@WLW?U#Z8` FYHd .^FY EX2[ DX $[3Y CX8Y"
5070       " 5YMY <Y1Y$Z KZ(ZCYCZ GY9Y IY@X@Y JY2Z L[3Z 3Y 6~W FZ<Z )[ 8Z 2Z KZ/Z;Z;Z*Z(Z)[=Z>[/[IuI[.\\  4X  4\\ =X =\\$\\"
5071       " =X      MZAU -Z &X8Y G~W 6X 0W<\\   FUEX MT iNW 8[D[ K~d  &T=WE\\<T     K~d HX  NQ<Y   4Y1Y CiEW 3Z    CV )k 7"
5072       "ZC[ HW7W5Y3W8XFh>Q<YAW5Z   KZ<Z ?Y;Y >Z<Z ?Y;Y >Z<Z ?Z<Y LZ=~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YBZ?Y*Z KZ/"
5073       "Z KZ0Z KZ1[ L[1Z KZ H[K\\ J[8X=[+Z(Z#Z(Z$Z(Z$Y'Y <` 5Z2Z KYDZ  ?X  Y  Y  NX  NX  NX 4[/Y,Z $Y/Y JY/Y JY/Y JY/Y "
5074       "6Y  Y  NX  Y 3Z3Z HZ3Y IZ1Z IZ2Z IZ2Z JZ1Z IZ2Z   ?Z:b IY0X HY0X GX0X GX0Y EY8Z E[2Y GZ8Z     ;W9X9X.W HW-XB[M"
5075       "\\BW,W3[BX.W HW =X 0Y8Z     ;~d NY;U   6~X!~[%~c&~Z LX0Y HX.X =ZJZ 7Y=Y N~l                  4Z    3Y    X@X ?`L"
5076       "T >eBX<U\"[M\\4Y   ;Y  CZ 7Q?V?Q 0X        .X 8Y-Z 5Z  H\\ 5j 9Y=Z ?T9_ Ec>] ,Z 1j <[7_     7i 8~d 7i 5[ KW=Z="
5077       "\\?W*Y:Y F{ FZ !Z\"Z\"~Q Dy![1j&~] :Z \"Z 4e &Z 2YDXCXDY(YBZ@Z*Z KZ\"Z<a&Z K['} <s ,Y 9Z(Z KZ9Z%ZAXBXAZ E] &_ $"
5078       "\\ (X  JY  EY        &Y F[2Z HZ %Y1[ IY.Y 9Y 4Z1Z GZ3Z 5Y  NX 0XF\\ %Y FZ4Z3Y+Z2Y IZ1Z I[2Z LY1Z ;[ +X  DY .Y1Y "
5079       "EY7Y NX@XKW@Y G[K[ :Y8Y ([ ,Z  NX  NY /[(R   NU?XNW=U%Z7_ EYHg 3bHY FY1Z DX $Z2Y CY:Y 5ZMZ =Y1Y$Z KZ)[CYBY GY9Y"
5080       " IY@X@Y JY1Y LZ1Z 4Y 6~W FY;Z *[ 7Z 2Z KZ/Z;Z;Z*Z(Z(Z=Z>[/[IuI[/\\  3X  3\\ >X >\\\"\\ >X      MZAU -Z 'X6X 5c "
5081       "%X 1X;\\   GUEX MT NgMW 9[D[ J~d  &T=m;T     K~d In 4TA[   4Y1Y BhEW 3Z    DX )i 5[D[ IX9W5Z3W8WFj?TA[BX5Z   KY"
5082       ";Z @Z;Z ?Y:Y @Z;Z ?Z;Y ?Y;Z NZ<~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YAY?Y*Z KZ/Z KZ1[ KZ1[ L[1Z KZ G[M\\ IZ8"
5083       "X<[+Z(Z#Z(Z$Z(Z$Y'Y <_ 4Z2Z KYD[  @X  NX  Y  NY  X  NX 3Z/Y-Z $Z/Y KZ/Y KZ/Y KZ/Y 6Y  Y  NX  Y 4Z2Z HZ3Y IZ1Z I"
5084       "Z1Z JY1Z JZ1Z IZ1Z   @Z;XNZ JY0X HY0X GX0X GX0Y EY8Y D[2Z GY8Y     ;X9X8W.W HW-W@hAW-X4[@W.W:[:W =X 0Z9Z      I"
5085       "[ 7Y<U   6~Y\"~^'~c'~\\ MX/X HX.X =YIZ 7Z>Y ~m                  4Z    3Y    W?X >g =cAW?]'[K\\5Y   ;Y  CZ %V  M"
5086       "X        /X 7Y-Z 5Z  H[ 4l ;X<Z ?Q4^ Fb<] .[ 3o ?[7_     :i    5j 9[ JW=Y;[?W+Z:Y F~ IZ !Z\"Z\"~Q Dy![2l'~] :Z "
5087       "\"Z 4f 'Z 2YDXCXDY(YAZAZ*Z KZ\"~%Z K['| 9s .Y 9Z(Z JZ:Z%ZAXBXAZ E] %] #[ 'X  IX  EY        &Y FZ0Y HY %Z1[ IY.Y"
5088       " 9Y 4Y0Z GZ2Y 5Y  NX 0XG[ #Y FZ4Z3Y+Z2Y JZ0Z IZ0Y MZ1Z ;Z *Y  EY .Y1Y EY8Z NYAXKXAY FZL[ 9Y9Y ([ +Y  MX  NZ 4b,"
5089       "S   U=`=U%Z6^ EYHi 6dIY FY1Z DY %Z2Y BX:Y 5ZLY =Y1Y%[ KZ)ZBYBZ HY9Y IY@X@Y JY1Z MZ1Z 4Y 6~W GZ:Y +\\ 7Z 2Z KZ/Z"
5090       ";Z;Z*Z(Z([>Z>Z.[IuI[0\\  2X  2\\ ?X ?\\ \\ ?X      MY@U 8y ;X6X 4a $X 1X9[   HUEX MT MeLW :[D[ I~d  &T=l:T     "
5091       "K~d Io 5m   3Y1Y AgEW 3Z    Nl 2g 3[D[%lDX5Z>mDXFk@mAW5[   LZ:Y @Y:Z ?Y:Y @Z:Y ?Y:Z AZ:Y NZ<~Q3Z  H~Q G~Q F~Q G"
5092       "~Q 5Z !Z !Z \"Z Ew5[)YAZ@Y*Z KZ/Z KZ1[ KZ1[ L[1Z K[ Gh HZ9X;[+Z(Z#Z(Z$Z(Z$Y'Y ;] 3Z2Z KYC[  AX  NX  Y  NY  Y  X"
5093       " 3Y.Y-Z $Y.Y KY.Y KY.Y KY.Y 6Y  Y  NX  Y 4Z1Y HY2Y IZ1Z IY0Z KZ0Z KZ1Z IY0Z   @Y;XMZ JY0X HY0X GX0X GX0Y DY9Y D"
5094       "Z0Y GY9Z     ;W8X8W.W HW-W?f?W.W4[?W.W:[:W =X 0Z9Y      HZ 5X<U   6~Z$~`'~a&~\\ NY/X HX.X =YHY 7Z?Z ~m         "
5095       "         4Z    3Y    W?W <i >_@XAa*[I\\6Y   ;Y  CZ %V  MX        /X 7Y-Z 5Z  I[ 3n >X;Z  ] G`9\\ .Z 4s @[9`    "
5096       " =i    /i ;Z IV=Y9Z>V+Z:Z G~P JZ !Z\"Z\"~Q Dy!Z1l'~] :Z \"Z 4g (Z 2YDYEXCY(YAZAZ*Z KZ\"}$Z K['z 5r /Y 9Z(Z JZ;Z"
5097       "$ZAW@WAZ F_ %\\ $[ &X  IX  EY        &Y FZ0Y IZ %Y/Z IY.Y 9Y 4Y0Z GY1Y 5Y  NX 0XH[ \"Y FY3Z3Y+Z2Y JZ0Z IZ0Y MY0"
5098       "Z ;Z *Z  FY .Y1Y DY9Y MYAWJXAY F[MZ 8Z:Y )[ +Z  MX  N[ 7g1U   U<^;U&Z6^ EYHj 9gJY FX/Y CY &Z2Y BY<Z 6ZKZ >Y1Y%Z"
5099       " J[*ZBYBZ HY9Y IY@X@Y KY0Z MY/Y 4Y 6~W GZ:Z ,[ 6Z 2Z KZ/Z;Z;Z*Z(Z([>Z?[.ZHuI[1\\  1X  1\\ @X @\\ M\\ @X      NZ"
5100       "@U 8y ;W4X 5` #X 1X8Z   HUEX MT LbJW ;ZC[ H~d  &T=j8U     L~d Io 5l   2Y1Y @fEW 3Z    Nl 0c 0[CZ&lDW5[>mEXE\\N^"
5101       "AlAX6\\   LZ:Z AY9Y @Z:Z AY9Y @Z:Z AY9Z!Z;~Q3Z  H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z K"
5102       "[ Ff GZ:X:[+Z(Z#Z(Z$Z(Z$Y'Y :\\ 3Z2Z KYC\\  BY  X  NX  NY  Y  X 3Y-X-Y #Y-X KY-X KY-X KY-X 6Y  Y  NX  Y 5Z0Y HY"
5103       "2Y IY/Y JZ0Z KZ0Z KY/Z KZ/Y   AZ;WKY JY0X HY0X GX0X GX0Y DY:Z DZ0Y FY:Y     :WK~KW.WK}KW-W>d>W.W5[>W.W:[:W =X /"
5104       "Y:Z      IZ 4Y=T   6~[%~b'~_%~\\ NY/X HX.X >ZHY 6Y?Y N~m                  4Z    3Y   !X@X ;l @[>WBe,ZG\\7Y   ;Y"
5105       "  CZ %V ;~c        LX 7Y-Z 5Z  J\\ 2n @Y;Z  N\\ G`8\\ /Z 5u A\\<b     ?i    *i ?Z IW=X8Z>V+Y8Y G~R LZ !Z\"Z\"~Q"
5106       " Dy![2l'~] :Z \"Z 4h )Z 2YCXEXCY(Y@ZBZ*Z KZ\"|#Z K['x 0q 1Y 9Z(Z IZ<Z$ZBX@XBY F` %[ $\\ &X  IX  EY        &Y FZ"
5107       "0Z JZ %Y/Z JY,X 9Y 5Z0Z GY1Y 5Y  NX 0XI[ !Y FY3Z3Y+Y1Y JZ/Y IZ0Y MY/Y ;Z *[  GY .Y1Y DY9Y MYBXIWBY Dg 7Y;Z *[ +"
5108       "[  MX  M[ :l6W   T:\\:U&Y5] DYHk :hKY GY/Z DZ 'Z2Y BY<Y 5ZKZ >Y1Y%Z IZ*YAYBZ HY9Y IY@X@Y KY/Y MY/Y 4Y 6~W GY9Z "
5109       "-[ 5Z 2[ LZ/Z;Z;Z*Z(Z'[?Z?[.[IuI[2~n BX B~n AX A~m AX      NZ@U 8y <X4X 4_ #X 1X7Z   IUEX MT J^HW <ZCZ F~d  &T="
5110       "g5T     -X ,o 5k   1Y1Y >dEW 3Z    Nl ._ ,ZCZ'lEX6\\>mEWDVCZBkAX6]   LY8Y BZ9Z AY8Y BZ9Z AY8Y BZ9Z!Z;~Q3Z  H~Q "
5111       "G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z KZ Ee FZ;Y:[+Z(Z#Z(Z$Z(Z$Y'Y :[ 2Z2Z KYB\\  CY  X  NX"
5112       "  NY  Y  Y 4Y-Y.Y #Y-X KY-X KY-Y LY-Y 7Y  Y  NX  Y 5Z0Z IY2Y JZ/Z KZ/Y KY/Z KY/Z KZ/Y#~d$Z<WJY JY0X HY0X GX0X G"
5113       "X0Y DZ;Y CZ0Y FY:Y     :WK~KW/WJ}JW.W=b=W.W6[=W/W9[9W >X /Z;Z      JZ 2X>U   6~\\'~c&~^$~Z MY/X HX.X >YGZ 7Z@Y "
5114       "N~m                  4Z    3Y   !X@X :n 'WBg.ZE\\8X   :Y  CZ %V <~e        NX 6Y-Y 4Z  K\\ #a AX:Z  M\\ H_6[ 0Z"
5115       " 6aI` A]?c     ?f    $f ?Z IW>Y7Y>V,Z8Z HZ8` MZ !Z\"Z\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZN] *Z 2YCXFYCY(Y@ZBZ*Z KZ\"{\"Z "
5116       "K['v +o 2Y 9Z(Z IZ<Z#YBX@XCZ Fa %Z %\\ %X  HX  FY        6i FZ0Z JZ %Y/Z JY,X 9Y 5Z/Y GY1Y 5Y  NX 0XK\\  Y FY3Z"
5117       "3Y+Y1Y JY.Y IY/Z NY/Y ;Z *\\  HY .Y1Y DZ;Z LXBXIWBY Ce 6Y;Y )[ -\\  LX  L\\ >q:X  !U:[9U&Y5] DY?d =jLX FY/Z C[ "
5118       ")Y1Y AX=Z 6ZIY >Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ/Z 5Y 5Y-Y HZ8Y .[ 4Z 1Z LZ/Z;Z;Z*Z(Z'[?Z@[-[ L[3~o BX B~o BX"
5119       " B~o BX      NZ@U 8y <X4X 4^ \"X 1X6Y   IUEX MT GW *ZCZ E~d  &T=g5T     -X ,o 5i   /Y1Y <bEW 3Z    Nl *W 'ZCZ(l",
5120       "EW6]>mFXDS?YBi?W5] CY 4Z8Y BY7Y BZ8Z CY7Y AY8Z CZ8Y!Y:Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z Ew5[)Y?ZBY*Z KZ/Z KZ1[ KZ"
5121       "1[ L[1Z KZ Dc E[=Y9[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z2Z KYB^ &i 0i 1i /i 0i 0i Ej-Y/Z $Z-Y MZ-Y MZ-Y LY-Y 7Y  Y  NX  Y 5Y/"
5122       "Z IY1X JZ/Z KZ/Z LY.Y LZ/Z KZ/Z$~d$Z=WIZ KY0X HY0X GX0X GX0Y CY<Z CY/Z GZ<Z     :WK~KW/WJ}JW.W<`<W.W7[<W/W9[9W "
5123       ">X .Y;Y      JZ 1Y?U   6~\\(~e'~]\"~X LX.X HX.X >YFY 7ZAZ N~m                  4Z    3Y   !W?X 9p +XCi0ZC\\9X  "
5124       " :Y  CZ %V <~e        NX 6Z.Y 4Z  L\\  M^ CY:Z  L[ H^4Z 0Z 7^A^ C_Ce     ?c     Mc @Z HW>X6Y>V,Y7Z HZ5^ NZ !Z\""
5125       "Z\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZM] +Z 2YBXGXBY(Y?ZCZ*Z KZ\"z![ LZ&w 'k 3Y 9Z(Z IZ=Z\"ZCX@XCZ Gc &Z &\\ $X  HX  FY   "
5126       "     >q FY.Y JY $Y/Z JY,X 9Y 5Y.Y GY1Y 5Y  NX 0XL\\  NY FY3Z3Y+Y1Y JY.Z JY/Z NY/Y ;Y (^  KY .Y1Y CY;Y KYCXIXCY "
5127       "Bc 4Y<Y *[ 2a  KX  La Du?Z  !U9Z8T'Z5] DY9^ >\\IYMX FY/Z B\\ +Y1Y AY>Y 5ZIZ ?Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ"
5128       "/Z 5Y 5Y-Y HZ8Z 0\\ 4Z 1Z LZ/Z;Z;Z*Z(Z&[@Z@[-[ L[4~p BX B~o BX B~p CX      NY?U 8y <W2W 3] \"X 1Y7Y   IUEX MT  "
5129       " JZCZ  8X  &T=WIZ6T     -X ,o 3e   -Y1Y :`EW 3Z    Nl   (ZCZ)lFW5UNV>mFWCQ;XAe>X6UNW CY 4Y7Z DZ7Y BZ8Z CY7Z CZ7"
5130       "Y CY7Z#Z:Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z :Z#[)Y?ZBY*Z KZ/Z KZ0Z KZ1[ L[1Z KZ Ca D[>Y8[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ "
5131       "KYA^ /q 9r 9q 7q 8q 9r Mq,Y/Z $Y,Y MY,Y MY,Y MZ-Y 7Y  Y  NX  Y 5Y.Y IY1X JZ/Z KY.Z LY.Y LZ/Z KY.Z$~d$Y=XIZ KY0X"
5132       " HY0X GX0X GX0Y CY<Y BY/Z FY<Y     9WK~KW/WJ}JW.W;^;W.W8[;W/W9[9W >X .Y<Z      K[ 1Y@U   6~](~f'~[ ~V KX.Y IX.X"
5133       " ?ZFY 6YAZ N~m                  4Z    3Y   !W?W 6p -WCk1ZB\\;Y   :Y  CZ %V <~e        NX 6Z.Y 4Z  M\\  J] EY9Z "
5134       " L[ H^4[ 2[ 8\\<\\ BbKi     ?`     Ha @Z HV=X5X>W-Y6Y HZ2\\ Z !Z\"Z\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZL] ,Z 2YBXGXBY(Y?Z"
5135       "CZ*Z KZ\"x N[ LZ&x #f 3Y 9Z(Z HZ>Z\"ZCW>WCZ Hd &Z &[ #X  HX  FY        At FY.Y JY $Y/Z JY,Y :Y 5Y.Y GY1Y 5Y  NX"
5136       " 0XM\\  MY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y (b  Y .Y1Y CY;Y KYCWHXCY Bb 3Y=Y *[ 6e  JX  Ke KzF^  !U9Y7T'Z4[ CY7] @[E"
5137       "XNX GZ.Y Ai 9Y1Y AY>Y 5YHZ ?Y1Y&[ IZ+ZAYAY HY9Y IY@X@Y KY/Y NZ.Y 5Y 5Y-Y IZ6Y 0[ 3Z 1Z LZ/Z;Z;Z*Z(Z&\\AZA[,[ L["
5138       "4~p BX B~o BX C~q CX      NY?U 8y <W2W 3\\   )Y6Y   JUEX NU   KZCZ  7X  &T=WGY7T     -X    J^   *Y1Y 7]EW 3Z   "
5139       "     8ZCZ 4X6UMV GX-X=^;W6UMW CY 4Y6Y DZ7Z CY6Y DZ7Z CY6Y DZ7Z#Z:Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z :Z#[)Y>ZCY*Z K"
5140       "Z/Z KZ0Z L[1[ L[1Z KZ B_ C[>X7[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY@_ 5u <u <t :t <u <u!t,Y/Y #Y,Y MY,Y MY,Y MY,Y 7Y  Y "
5141       " NX  Y 6Z.Y IX0X JY-Y KY.Z MZ.Y LZ.Y KY.Z$~d$Y>XHZ KY0X HY0X GX0X GX0Y BY=Y BY.Y FY=Z     9WK~KW/WJ}JW.W:\\:W.W"
5142       "9[:W/W9[9W >X .Z=Y      JZ /X@U   6~^*~g&~Y N~V KX.Y IX.X ?ZFZ 7ZBY L~l                  4Z    3Y   \"X@X 3n /X"
5143       "CZIZ2Z@\\<Y   :Y  BY %V <~e        Y 6Z.Y 4Z  N\\  G\\ FX8Z  K[ I]2Z 2Z 8\\9[ BsNZ     ?]     B^ @Y GV=W4X>W.Z6"
5144       "Z IZ1[ Z !Z#[\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZK] -Z 2YBXHYBY(Y>ZDZ*Z KZ\"v L[ LZ&z !c 4Y 9Z(Z HZ>Z\"ZDX>XDY Ge 'Z '[ "
5145       "\"X  GX  GY        Dw FY.Y JY %Z/Z J~W :Y 5Y.Y GY1Y 5Y  NX 0XN\\  LY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y 'e $Y .Y1Y CZ=Z"
5146       " KYDXGWDY @a 3Z>Y +[ 5d  IX  Ic L~d  !U8X7T'Z4[ CY5\\ AZCa GY-Y @h 9Y1Y @X?Z 6ZGY ?Y1Y&[9X9Z+ZAYAZ IY9Y IY@X@Y "
5147       "KY/Z Y-Y 5Y 5Y.Z IZ6Z 2[ 2Z 1Z M[/Z;Z<[*Z(Z%[AZB\\,[ LZ3~p BX B~o BX C~q CX      NY?U 8y <W2W 2[   (Y7Y   ITDW "
5148       "NU   M[CZ  6X  &T=WFY8T     -X        EY1Y 1WEW 3Z        7ZC[ 6W6ULV HX+W JX7ULW CY 5Z6Z EY5Y DZ6Z EY5Y DZ6Z E"
5149       "Z6Y$Z9Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z :Z#[)Y>ZCY*Z KZ/Z KZ0Z L[1[ L[1[ LZ A] B[?X6Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY?"
5150       "_ 8w ?x ?w =w >w >w$~u/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY-Y$~d$Y?XFY KY0X HY0X GX0"
5151       "X GX0Y BY>Z BY.Y EY>Y     8WK~KW/WJ}JW.W;]:W.W:[9W/W9[9W >X -Y>Z      KZ .YAU   6~^*~g%~W L~T JX.Y IX.X ?YEZ 7Z"
5152       "CZ L~k                  :y    KY   \"X@X 0m 1WCYEY3Y>\\=X   9Y  BY %V <~e   =l    X 5Z.Y 4Z  \\  E[ GY8Z  JZ I]"
5153       "2Z 2Z 8[7[ BqMZ     ?^     C^ @Y GV=W4X>V-Y5Z IZ0[!Z !Z#[\"Z  MZ 1[2l'Z(Z :Z \"Z 4ZJ] .Z 2YAXIXAY(Y=YDZ*Z L[\"s"
5154       " I[ LZ&[Cc  Na 5Y 9Z(Z HZ?Z YDX>XEZ Hg (Z (\\ \"X  GX  GY        Fy FY.Y KZ %Z/Z J~W :Y 5Y.Y GY1Y 5Y  NX 0e  KY"
5155       " FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y &h (Y .Y1Y BY=Y IXDXGWDY ?_ 1Y?Z ,[ 4b  GX  Ga L~c   T6V6T'Z4[ CY4\\ CZ@_ GY-Y >f "
5156       "9Y1Y @Y@Y 5YFZ @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z IY5Z 3[ 1Z 1Z M[/[<Z<[*Z(Z%\\BZC\\+[ LZ3~p BX B~o "
5157       "BX C~q CX    DX 4Z?U -Z (W2W 2Z   'Z7X   ITDX U   MZCZ  5X  &U>WEY9T     -X        EY1Y 1WEW 3Z        6ZCZ 7X7"
5158       "UKV HW*W KX6ULW CY 5Y5Z FZ5Z EY4Y FZ5Z EZ5Y EY5Z%Z9Z <Z  HZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z KZ0Z L[0Z "
5159       "LZ0[ LZ A] B[@X5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z4[ JY>` <y @y Ay ?y @y @y%~v/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Z.Y IX0X JY"
5160       "-Y KY-Y MZ.Z MY-Y KY-Y$~d$Y?WEY KY0X HY0X GX0X GX0Y BZ?Y AY.Y EY>Y     8WK~KW/WJ}JW.W<_;W.W;[8W/W9[9W >X -Z?Z  "
5161       "    LZ -YBU   5~^*~h%~U J~R IX.Y IX.X @ZDY 6YCZ LW                   'y    JY   \"W?X ,j 3WCYCY4Y=\\>X   9Y  CZ"
5162       " %V <~e   =l    X 5Z.Y 4Z !\\  C[ IY7Z  JZ I]2Z 3[ 9[5[ BoLZ     ?a     Ia @Y HW>X3W>V.Z4Y IZ/Z!Z !Z#[\"Z  MZ 0"
5163       "Z Z'Z(Z :Z \"Z 4ZI] /Z 2YAXIXAY(Y=ZEZ*Z L[\"o DZ LZ&Z<^  M_ 5Y 9Z(Z GZ@Z ZEX>XEZ I[MZ (Z )\\ !X  GX  GY        "
5164       "Gz FY.Y KZ %Y-Y J~W :Y 5Y.Y GY1Y 5Y  NX 0c  IY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y %j +Y .Y1Y BY=Y IYEXGXEY >] 0Y?Y ,[ "
5165       "3`  EX  E_ L\\Cx   NT6V6T'Z4Z BY2Z CY>^ GY-Y ;c 9Y1Y @YAZ 6ZEY @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ"
5166       "4Y 4\\ 1Z 1[ NZ.[<Z<Z)Z(Z$\\CZD]*Z LZ3~p BX B~o BX C~q CX    DX 4Z?U -Z (W2W 2Z   'Z7X   ITDX U   MYBY  4X  &U>"
5167       "WDX:U     -X        EY1Y 1WEW 3Z        5YBY 7W6UKV IX*W KW6UKW CY 6Z4Y FZ5Z FZ4Z GZ4Y EY4Z GZ4Y%Y8Z <[  IZ !Z "
5168       " Z !Z  >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z L[0Z L[0Z LZ0[ LZ B_ BZAY5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY=` ?{ B{ Bz @z B{ "
5169       "B{'~x/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Z.Y IX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y$~d$Y@WDY KY0X HY0X GX0X GX0Y AY@Z AY.Y "
5170       "DY@Z     8WK~KW/WJ}JW.W=a<W.W<[7W/W9[9W >X ,Y?Y      LZ +XBU   6~_+~i%~U I~P HX.Y IX.X @ZDZ 7YCY KX            "
5171       "       (y    JY   \"W?W (h 5XCXAX5Z<\\@Y   9Y  CZ $T ;~e   =l    X 5Z/Z 4Z \"\\  AZ IX6Z  JZ I\\1[ 4Z 8Z3Z AmKZ"
5172       "     ?d     d AZ HW>X3W>V.Z4Z JZ.Z\"[ \"Z#[\"Z  MZ 0Z Z'Z(Z :Z \"Z 4ZH] 0Z 2YAYKX@Y(Y<ZFZ*[ M[\"Z /Z LZ&Z:\\  K"
5173       "^ 6Y 9Z(Z GZAZ NZEW<WEZ IZL[ )Z *\\  X  FX  HY        H{ FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y  NX 0c  IY FY3Y2Y+Y1Y"
5174       " KZ-Y JY.Y Y-X ;Y $l .Y .Y1Y AY?Y HYEWFXEX =\\ .Y@Y -[ 2b  GX  Ga LY=s   LT6W7T'Z4Z BY2Z DY=^ GY-Z =d 9Y1Y ?XAY"
5175       " 5YDZ AY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ4Z 5[ 0Z 0Z NZ-Z<Z<Z)Z(Z#\\DZD\\)Z LZ3~p BX B~o BX B~p CX"
5176       "    DX 4Z?U -Z (W2W 2Z   &[9X   IUEX T s AXAY  4X  &U>WCX;U     -X        EY1Y 1WEW 3Z      Is 0YAX 8W6UJV IW)W"
5177       " LX7UJW CY 6Z4Z GY3Y FZ4Z GY3Y FZ4Z GY3Z'Z8Z <[  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z#Z(Y<ZEY*[ M[/[ M[0Z LZ/Z LZ/Z LZ "
5178       "Ca CZBY4Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY<` A| C| C{ A{ C| C|(~y/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Y-Z JX0X JY-Y LZ-Y"
5179       " MZ.Z MY-Y KY-Y$~d$YAWCY KY0X HY0X GX0X GX0Y AY@Y @Y.Y DY@Y     7WK~KW/XK}KX.W>c=W.W=[6W/X:[:X >X ,Y@Z      M[ "
5180       "+YCT   5~`,~i$~S H~P HX.Y IX.X @YCZ 7ZDY KX                   )y    HX   #X@X (TNc 6WCX@X5Y:\\AX   8Y  CZ   :~e"
5181       "   =l   !X 4Z/Z 4Z #\\  @[ KY6Z  IZ I[0Z 4Z 9Z2[ @jJZ     ?f    %g AZ HW>X3W>V.Y2Y JZ.Z\"[ \"Z#Z!Z  MZ 0Z Z'Z(Z"
5182       " :Z \"Z 4ZG] 1Z 2Y@XKX@Y(Y<ZFZ*[ MZ!Z /Z LZ&Z8[  K] 6Y 9Z(Z FZBZ NZFX<XFY I[KZ )Z +\\  NX  FX  HY        I| FY."
5183       "Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y  NX 0d  JY FY3Y2Y+Y1Y KZ-Y JY.Y Y-X ;Y #m 0Y .Y1Y AY?Y HYFXEWFY =\\ .YAY ,[ 2d  I"
5184       "X  Ic LW8n   JU7W7T'Y2Y BY1Z EY<\\ FY-Z @g 9Y1Y ?YBY 6ZDZ AY1Y&Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Y.Z JY3Z 6["
5185       " /Z 0Z [-[=Z=[)Z(Z#]EZE\\(Z LZ2~o BX B~n AX A~n BX    DX 4Z?U -Z (X4X H~W   <\\:W   HUDX!T s AZCZ  5X  %T>WBX<U"
5186       "     -X        EY1Y 1WEW       \"s 1ZCZ 9X7UIV JX)W LW7UIW CY 6Y2Y HZ3Z GY2Y HZ3Z GY2Y HZ3Z'Z8Z <[  IZ !Z  Z !Z"
5187       "  >Z !Z !Z \"Z :Z#Z(Y<ZEY)Z M[/[ M[0[ MZ/Z LZ/Z M[ Dc DZCY3Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z6\\ IY:` D} D} D| B| D} D})~z"
5188       "/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y$~d%ZBXCY KY0X HY0X GX0X GX0Y @YAY @Y.Y DYAZ "
5189       "    7W8X8W.W HW-W?e>W.W>[5W.W:[:W =W +ZAY      LZ *YDU   5~`,~i#~Q F} GX.Y IX.X AZBY 7ZEZ KX                   "
5190       ")y    HX   6~e 9TJ_ 7XCX?X6Y9\\BX   8Y  CZ    KX    Nl   !X 4Z/Z 4Z $\\  >Z LY5Z  IZ I[0Z 5Z 8Z1Z >fHY     =h  "
5191       "  +i @Z HW>X3W?W/Z2Z KZ.[#[ \"Z#Z!Z  MZ 0Z Z'Z(Z :Z \"Z 4ZF] 2Z 2Y@XLY@Y(Y;ZGZ*[ MZ!Z /Z M[&Z7[  K\\ 6Y 9Z(Z FZ"
5192       "BZ MYFX<XGZ J[IZ *Z +[  MX  FX  HY        Jb>Y FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y  NX 0e  KY FY3Y2Y+Y1Y KZ-Y JY.Y"
5193       " Y-X ;Y !m 2Y .Y1Y AZAZ GYGXEXGY >] .ZBY -[ 1e  JX  Ke LU4k   IU8Y8T'Y2X AY0Y EX:[ FY-Z Ah 9Y1Y >XCZ 6YBY AY1Y&"
5194       "Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y JZ2Z 8[ .Z 0[!Z,[=Z=[)Z(Z\"]FZG]'Z M[1]  1X  1\\ @X @\\ L\\ AX    DX 4"
5195       "Z?U -Z (X4X H~W   ;\\;W   GTDX\"U s A[D[  6X  %T>WBX<T     ,X        EY1Y 1WEW       \"s 2[D[ 9W7UHV KX(W MX7UI"
5196       "W CY 7Z2Z IZ3Z HZ2Z IZ3Z HZ2Z IZ3Z(Z7Z ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z$[(Y;ZFY)Z M[/[ MZ/[ MZ/Z M[/Z M[ Ee EZC"
5197       "X3[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z8^ IY9` Fb=Y Eb=Y Eb=X Cb>Y Eb=Y Eb=Y*b=~V/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Y-Z JX0X JY"
5198       "-Y LZ-Y MY-Z MY-Y LZ-Y   CZCXBY KY0X HY0X GX0X GX0Y @YBZ @Y.Y CYBY     6W8X8W.W HW-W@g@X.W?[4W.W:[:W =W *YBZ   "
5199       "   MZ (XDU   5~`,~i\"~ D{ FX.Y IX.X AZBZ 7YEY IX                   +y    GX   6~e 9TG] 8WBW>X6Y8\\DY   8Y  CZ  "
5200       "  KX    Nl   !X 4Z/Z 4Z %\\  =Z LX4Z  IZ I[0Z 5Z 9Z0Z <bFY     ;i    1i =Z HW>X3W?W/~S KZ-Z\"Z \"Z#Z!Z  MZ 0[!Z"
5201       "'Z(Z :Z \"Z 4ZE] 3Z 2Y?XMX?Y(Y;ZGZ)Z MZ!Z /[ N[&Z6[  K\\ 7Y 9Z(Z FZCZ LZGX<XGZ JZH[ +Z ,\\  MX  FY  IY        K"
5202       "]8Y FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y  NX 0f  LY FY3Y2Y+Y1Y KZ-Y JY.Y Y-X ;Y  Mk 3Y .Y1Y @YAY FYGWDXGY >^ .YCZ ."
5203       "[ )_  KX  L_ ES/e   FU8Z9T'Z3X AY0Y FY:[ FY-Z Cj 9Y1Y >XCY 6ZBZ BY1Y&Z8X9[,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y J"
5204       "Z2Z 9\\ .Z /Z!Z,\\>Z>[(Z(Z!]GZH^'[ N[0\\  1X  2\\ ?X ?[ M\\ @X    DX 4Z?U -Z 'W4W G~W   :]>X   GTDY#U s @[D[  7"
5205       "X  %U?WAX>U     ,X        EY1Y 1WEW       \"s 3ZC[ 9X7UHV KW(W MX7UHW CY 7~S J~S H~S I~S I~S I~S)} ;Z  IZ !Z  Z"
5206       " !Z  >Z !Z !Z \"Z :Z$[(Y;ZFY)Z MZ-Z MZ/[ N[/[ N[/Z MZ Eg F[EX2[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z9^ HY7_ G]8Y F^8Y F^8X D]8"
5207       "Y E]8Y F^8Y+^8~V/Y #~W M~W M~W M~W 7Y  Y  NX  Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y   BYDXAY KY0X HY0X GX0X GX0Y"
5208       " @ZCY ?Y.Y CYBY     5W9X8W.W HW-WAiAW,WA[3W.W9Y9W >X *ZCZ     6~d IYET   4~`,~i!| By EX.Y IX.X AYAZ 7ZFY IX    "
5209       "                Z    3X   6~e 9TF\\ 9WBX=W7Z7\\EX   7Y  CZ    KX    Nl   \"X 3Z/Z 4Z &\\  ;Z M~Z %Z I[0Z 6[ 9Z/"
5210       "Y 8ZCZ     8i 6~d 5i ;Z HW>X3W?W0~T KZ-Z\"Z \"Z$[!Z  MZ 0[!Z'Z(Z :Z \"Z 4ZD] 4Z 2Y?XMX?Y(Y:ZHZ)Z N[!Z /[ NZ%Z6["
5211       "  J[ 7Y 9Z(Y DZDZ LZGW:WGZ K[GZ +Z -\\  LX  EX  IY        L\\6Y FY.Y KZ %Y-Y K~W 9Y 5Y.Y GY1Y 5Y  NX 0XM\\  MY "
5212       "FY3Y2Y+Y1Y KZ.Z JY.Y Y-X ;Y  Ji 4Y .Y1Y @YAY FYGWDXGX >` /YCY .[ $\\  LX  M\\ AR+`   CT9[:U'Z3X AY0Y FY9Z FY-Z "
5213       "D` .Y1Y >YEZ 6YAZ BY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y LY.Z Y-Y 5Y 5Z/Y KZ1Z 9[ -Z /Z\"[+[>Z>[(Z(Z ^IZJ_&[ NZ.\\  2X  3"
5214       "\\ >X >[ \\ ?X    DX 4Z?U -Z 'X6X G~W   9^@X   GUDY$T Ns ?[CZ  8X  %U?WAY?U     ,X        EY1Y 1WEW       \"s 4"
5215       "ZCZ 7W7UGV LX)X MW7UGW CY 8~T J~T I~S J~T I~T K~T*~ ;Z  IZ !Z  Z !Z  >Z !Z !Z \"Z :Z$[(Y:ZGY)[ NZ-Z N[.Z N[/[ N"
5216       "[/[ NZ Fi G[FX1Z)Z(Z#Z(Z$Z(Z$Z)Z 9Z 2Z<a HY5^ I[5Y F[5Y G\\5X E\\6Y F\\6Y F[5Y+[5~V/Y #~V L~V L~W M~W 7Y  Y  NX"
5217       "  Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y   BYDW@Y KY0X HY0X GX0X GX0Y ?YDZ ?Y.Y BYDY     4W9X9X.W HW-XC\\L[BW,WB["
5218       "3X.W HW >X )YCY     5~d IYFU   4~`,~i!{ @x EX.Y IX.X AY@Y 7ZGZ IX                    Z    3X   6~e 9TD[ ;XBX=X8"
5219       "Z6\\GY   7Y  CY    JX    Nl   \"X 2Y/Z 4Z '\\  :Z M~Z %Z I[0Z 6Z 8Z/Z \"Z     5i 9~d 8i 8Z HW>X3W?W0~U LZ-Z\"[ "
5220       "#Z$[!Z  MZ /Z!Z'Z(Z :Z \"Z 4ZC] 5Z 2Y?XNY?Y(Y:ZHZ)[ [!Z .Z NZ%Z5[  K[ 7Y 9Z(Y DZDY KZHX:XHY K[EZ ,Z .\\  KX  EX"
5221       "  IY        LZ4Y FY.Y KZ %Z.Y KZ  <Y 5Y.Y GY1Y 5Y  NX 0XL\\  NY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y  Ff 5Y .Y1Y @ZCZ FY"
5222       "HXCWHY ?b /YDY /[ ![  MX  M[ @Q%W   ?T9\\;U'Z3X AY0Z GX8Z FY-Z E\\ )Y1Y =XEY 6Z@Y BY1Y&Z9Y9[,ZAYAZ IY9Y IY@X@Y "
5223       "LY.Z Y-Y 5Y 4Y/Y KZ0Z ;[ ,Z /[#Z*\\?Z?\\(Z(Z N`LZL`$Z NZ-\\  3X  4\\ JPCXCP J[\"\\ >X    DX 4Z?U -Z 'X6X G~W   "
5224       "8^BX   FUDY%U Ns =ZCZ  9X  $U@W@X?T     +X        EY1Y 1WEW       \"s 5ZCZ 7W7UFV LW(W MX8UFW CY 8~U K~T J~U K~"
5225       "T J~U K~T*~ ;[  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z$Z'Y9YGY)[ [-[ [.Z N[.Z NZ.[ NZ G\\L[ GZGX0Z)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2~ "
5226       "GY4] J[4Y G[4Y G[4X EZ4Y FZ4Y G[4Y,[4X 1Y #Y  Y  Y  Y  9Y  Y  NX  Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y   BYEW?Y"
5227       " KY0X HY0X GX0X GX0Y ?YDY >Y.Y BYDY     4W9X9W-X JX,WD\\J[CW,WC[2W-X JX >X )YDZ     5~d HXFU   4~_+~i z @w DX.Y"
5228       " IX.X BZ@Y 6YGZ IY                    Y        @~e 9TCZ ;WAX=X8Y4\\HX   6Y  CY    JX    Mj   !X 2Y/Y 3Z (\\  9Z"
5229       " M~Z %Z I[0Z 6Z 8Z/Z \"Z     2i <~d ;i 5Z HW>X3W@W/~U LZ-[#[ #Z$Z Z  MZ /Z!Z'Z(Z :Z \"Z 4ZB] 6Z 2Y>a>Y(Y9ZIZ)[ "
5230       "Z Z .Z [%Z4Z  JZ 7Y 9Z)Z DZEZ JYHX:XIZ KZD[ -Z /\\  JX  EX  IY        MZ3Y FY.Y JY %Z/Z JY  <Y 5Y.Y GY1Y 5Y  NX"
5231       " 0XK\\  Y FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y  Bc 6Y .Y1Y ?YCY DYIXCXIY @c /YEY /[  NZ  MX  N[     *U;^=U&Z4Y AY/Y HY7X"
5232       " EY-Y E[ 'Y1Y =YFY 6Z@Z CY1Y&Z9Y9Z+ZAYAZ IY9Y IY@X@Y LZ/Z Y-Y 5Y 4Y0Z KZ0Z <[ +Z .Z$[)\\@Z@\\'Z(Z M~Q#Z [,\\  4"
5233       "X  5\\ JRDXDR J[$\\ KQCXDQ   #Y 4Z?U -Z &X8X F~W   7_EY   EUDY&U Ns <ZCZ  :X  $U@W?XAU     +X        EY1Y 1WEW "
5234       "      \"s 6ZCZ 7X8UEV MX)X MW7UFW DZ 8~U L~V K~U L~V K~U K~U+~ :Z  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['Y9ZHY(Z [-[ Z"
5235       "-[ Z-Z [-Z [ H\\J[ HZHY1[)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2} FY2\\ KZ3Y GZ3Y GY3Y FZ3Y GZ3Y GZ3Y,Z3X 1Y #Y  Y  Y  Y  9Y  Y  "
5236       "NX  Y 6Y-Z JX0X JY-Y KY.Z MZ.Z MY-Y KY-Y   BYFX?Y KY0X HY0X GX0X GX0Y >YEY >Y.Y BYEZ     4X:X9W,W JW+WE\\H[EX,X"
5237       "E[1W,W JW =X )ZEY     4~d HYHU   2~^+~i Nx >u CX.Y IX.X BY?Z 7ZHY GX                    Z        A~e 9TCZ <XAW<"
5238       "X8Z4\\JY   6Z  DY    JX        4X 1Z0Y 3Z )\\  8Z M~Z %Z I[0Z 7Z 7Z/Z \"Y     /i >~d >i 2Z GV>X3W@W0~V LZ-[\"Z "
5239       "#Z%[ Z  MZ /[\"Z'Z(Z :Z \"Z 4ZA] 7Z 2Y>a>Y(Y9ZIZ(Z Z Z .[![%Z4[  KZ 7Y 9Z)Z CZFZ JZIX:XIZ L[CZ -Z /[  IX  DX  J"
5240       "Y        MY2Y FY.Y JY %Z/Z JY  <Y 5Y.Y GY1Y 5Y  NX 0XJ\\ !Y FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y  ?a 7Y .Y1Y ?YCY DYIWBX"
5241       "IY @d /YFY 0[  LY  MX  NZ     )U<VNW=U&Z4Y AY/Y HY8Y EZ.Y F[ &Y1Y =YGZ 7Z>Y CY1Y&Z9Y9Z+ZAYAY HY9Y IY@X@Y LZ/Y N"
5242       "Y-Y 5Y 4Y0Z LZ.Y =[ *Z .[%Z(]AZA]'Z(Z L~\"[![+\\  5X  6\\ JTEXET J[&\\ KSDXES   $Y 3Y?U -Z &Y:Y F~W   5_GX   DU"
5243       "CZ9QAU   DZCZ  ;X  $VAW?YBU     +X        EY1Y 1WEW          DZCZ 6W7UEV NX)X MX8UEW DY 8~V L~V L~W M~V K~V M~V"
5244       ",~P :Z  JZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['Y8ZIY(Z Z+Z Z-[![-[![-[![ I\\H[ I[JY0[(Y(Z#Z(Z$Z)Z#Z)Z 9Z 2| EY1\\ LY2Y "
5245       "HZ2Y HZ3Y FY2Y GY2Y GY2Y-Z2X 1Y #Y  Y  Y  Y  9Y  Y  NX  Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY.Z   BYGX?Z KY1Y HY0X"
5246       " GX0X GX0Y >YFZ >Y.Y AYFY     2W:X:X,W JW+XG\\F[FW+XF[1X,W JW =X (YEY     4~d GXHU   2kNRMk*tNq Mv <s BX.Y IY/X"
5247       " BY>Y 7ZIZ GY                   !Z        A~e 9TBY <W@W;W8Z3\\KX   5Z  DY    JX        4X 1Z1Z 3Z *\\  7Z M~Z %"
5248       "Z HZ0Z 7Z 7Y.Z #Z     ,i A~d Aj 0Z GV=W4X@W0~W MZ-[\"[ $Z%[ Z  MZ /[\"Z'Z(Z :Z \"Z 4Z@] 8Z 2Y>`=Y(Y8ZJZ([\"[ Z "
5249       ".[!Z$Z3Z  KZ 7Y 9Z)Z CZGZ IZIW8WIZ M[AZ .Z 0\\  IX  DX  JY        MY2Y FY.Y JY $Y/Z JY  <Y 5Z/Y GY1Y 5Y  NX 0XI"
5250       "\\ \"Y FY3Y2Y+Y1Y JY.Z JY.Y NY/Y ;Y  ;] 7Y .Y1Y >YEY CYIWBXIX @f 0YGZ 0[  LZ  NX  NY     'U>WMW?V&Z4Y AY/Y HY8Y"
5251       " EZ.Y FZ %Y1Y <XGY 6Z>Y CY1Y&[:Z:Z+ZAYAY HY9Y IY@X@Y LZ/Y NZ.Y 5Y 4Y0Y KZ.Z ?\\ *Z -['['\\AZB]&Z(Z K|![!Z)\\  6"
5252       "X  7\\ JVFXFV J[(\\ KUEXFU   %Y 3Y?U -Z %Y<Y /Z    M`KY   BUC[=SAU   CZCZ  <X  #UAW>XCU     *X        EY1Y 1WEW"
5253       "          F[CZ 6X8UDV NW)X MX8UDW DY 8~W N~W L~W M~V L~W M~W-~P :[  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z%['Y8ZIY([\"[+["
5254       "\"[,Z![-[!Z,[!Z I\\F[ J[KY/Z'Z)Z#Z)Z#Z)Z#Z)Z 9Z 2{ DY0[ MY1Y HY1Y HY2Y FY2Y HZ2Y HY1Y-Y2Y 1Z $Y  Y  Y  Z  :Y  Y"
5255       "  NX  Y 6Z.Y IX0X JZ.Y KY.Z MZ.Y LZ.Y KY.Z   BYHX>Z KY1Y HY1Y GX0X GX0Y =YGY =Y.Y AYFY     2X;X:W+X LX*WH\\D[HX"
5256       "*WG[0W+X LX =X (YFZ     4~d GYIU   2jLQLj*pNRNq Lt :q AX.Y IY0Y CZ>Y 6YIZ FX                   !Z        A~e 9T"
5257       "BZ >W?W;W8Z2\\MY   4Y  DY    JX        4X 1Z1Z 3Z +\\  6Z M~Z %Z HZ0Z 8[ 7Y.Z #Z     )i D~d Ci -Z GV=W4XAW/~W M"
5258       "Z-[\"[ $Z&[ NZ  MZ .Z\"Z'Z(Z :Z \"Z 4Z?] 9Z 2Y=_=Y(Y8ZJZ([\"[ Z -Z\"[$Z3[  L[ 8Y 9Z)Z BZHZ IZJX8XJY LZ@[ /Z 1\\"
5259       "  HX  DX  JY        NY1Y FZ0Z JY $Y/Z JY  <Y 5Z0Z GY1Y 5Y  NX 0XH\\ #Y FY3Y2Y+Y1Y JY.Y IY/Z NY/Y ;Y  9\\ 8Y .Y1"
5260       "Y >YEY BXJXAWJY A[N[ 1YGY 0[  JY  NX  NY     'V@WLX@U$Y5[ BY/Y HX7X DZ.Y FY $Y1Y <YIZ 6Y=Z DY1Y&[:Z:Z*YAYAY HY9"
5261       "Y IY@X@Y LZ/Y NZ/Z 5Y 3Y1Y KY-Z ?[ )Z -[([%]CZC]%Z(Z Jy M[#[(\\  7X  8\\ JXGXGX J[*\\ KWFXGW   &Y 3Y?U -Z %Z>Z "
5262       "/Z    K_MZ   BUC]BVBU   A[D[  >X  #VBW=XDU     *X        EY1Y 1WEW          G[D[ 5W8UCV X*X LW8UCW EZ 8~W N~X M"
5263       "~W N~X M~W N~X.~Q :[  KZ !Z  Z !Z  >Z !Z !Z \"Z :Z&[&Y7ZJY([\"[+[\"[,[\"Z+[#[+Z\"[ J\\D[ JZKX/['Z*[#[*Z#Z)Z#Z)Z"
5264       " 9Z 2z CY/Z MY1Y HY2Z HY2Y GY1Y HY1Y HY1Y-Y2Z 2Z $Z !Z !Z !Z  :Y  Y  NX  Y 6Z.Y IX0X JZ/Z KY.Z LY.Y LZ/Z KY.Z  "
5265       " BYHW=Z KY1Y GX1Y GX1Y GX0Y =YHZ =Y/Z @YHY     1X;X;X*W LW)XJ\\B[IX*XI[0X*W LW <X (ZGY     3~d GYJU   1iKQKi*pN"
5266       "RMo Jr 9q AX.Y HX0Y CZ>Z 7ZJY EY                   !Z        1X@X &TAY ?X?W;W8Z1\\NX   3Y  DY    JX        5Y 0"
5267       "Y1Z 3Z ,\\  5Z M~Z %Z HZ0Z 8Z 6Y.Z #Z     &i G~d Fi )X FV=X5XAW0~Y NZ-[!Z $Z&[ NZ  MZ .[#Z'Z(Z :Z \"Z 4Z>] :Z 2"
5268       "Y=_=Y(Y7ZKZ'Z#[ NZ -[#[$Z2[  M[ 8Y 9Z)Z BZHZ HYJX8XKZ M[?Z /Z 2\\  GX  CX  KY        NY1Y FZ0Z JZ %Y/Z JZ  =Y 4"
5269       "Y0Z GY1Y 5Y  NX 0XG\\ $Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y  8[ 8Y .Y1Y >ZGZ BYKXAXKY B[LZ 0YHY 1[  IY  NX  Z     &VB"
5270       "XKXBV$Y5[ BY/Y HX8Y CY/Z GY #Y1Y <YIY 6Z<Y DY1Y%Z:Z:Z*YAYAY HY9Y IY@X@Y LZ/Y NZ/Z 5Y 3Y2Z LZ,Z A[ (Z ,[)[%^DZD^"
5271       "%Z(Z Iw L[#['\\  8X  9\\ JZHXHZ J[,\\ KYGXHY   'Y 3Z@U -Z $[B[ .Z  NW $j   @UCpBU   @[D[  ?X  \"UBW=XEU     )X "
5272       "       EY1Y 1WEW          H[D[ 5W8UBV W*X LX8UCW F[ 9~Y ~X N~Y ~X N~Y ~X.~Q 9[  LZ !Z  Z !Z  >Z !Z !Z \"Z :Z&[&"
5273       "Y7ZJY'[#Z)Z#[+[#[+[#[+[#[ K\\B[ K[MX.['Z*Z!Z*Z#Z)Z#Z)Z 9Z 2x AY.Z NY2Z HY2Z IY1Y GY1Y HY1Y HY2Z-X1Z 2Z $Z !Z !Z"
5274       " !Z  :Y  Y  NX  Y 5Y/Z IX0X JZ/Z KZ/Y KY.Y LZ/Z KZ/Y   AYIW<Y IX1Y GX1Y GX1Y GY2Z =YHY <Z0Y ?YHY     0X<X;X*X N"
5275       "X)XJ[@[KX(XK[/X*X NX <X 'YHZ     3~d FXJU   0hKQKh(nMRMo Jq 7o @X.Y HX0X BY=Z 7ZKZ DY                   \"Z    "
5276       "    1W?X &TAY ?W>W;W8Z0e   3Y  EZ    JX        5X /Z2Y 2Z -\\  4Z M~Z %Z HZ0Z 8Z 6Z/Z $Z     #j J~d Ii   CW>X6Y"
5277       "BX0~Y NZ-[![ %Z'\\ NZ  MZ -Z#Z'Z(Z :Z \"Z 4Z=] ;Z 2Y<]<Y(Y7ZKZ'[$[ NZ -[$[#Z1Z  M[ 8Y 8Z*Z BZIZ GZKX8XKZ N[>[ 0"
5278       "Z 3\\  FX  CX  KY        NY2Z FZ0Y IZ %Y/Z JZ  =Y 4Y0Z GY1Y 5Y  NX 0XF\\ %Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y  7Z 8Y"
5279       " .Y2Z =YGY AYKW@XKY BZJZ 1YIY 1[  HY  NX  Y     %WEYIYFW#Y5[ BY/Y HX8Y CY/Z GY #Y1Y ;XIY 6Y;Z EY1Y%Z:Z:Z*ZBYBZ "
5280       "HY9Y IY@X@Y LZ/Y MY/Z 4Y 4Y2Y KZ,Z B[ 'Z +[+[#_FZF_$Z(Z Gt JZ$[%\\  9X  :\\ J\\IXI[ I\\/\\ K[HXI[   (Y 3Z@U -Z "
5281       "%^F^ /Z  X \"f   >VBnCU   >[D[  @X  \"VCW<XGV     )X        EY1Y 1WEW          I[D[ 5X8UBV!W)X LW8UBW FZ 8~Y!~Z"
5282       " ~Y!~Z ~Y ~Y0~R 9[  LZ !Z  Z !Z  >Z !Z !Z \"Z :Z'[%Y6ZKY'[$[)[$[*[$[*[%[*[$[ K\\@[ Le.[&Z*Z!Z*Z\"Z*Z#Z*[ 9Z 2v "
5283       "?Y.Z NY2Z HX1Z IY1Y GY1Y HY2Z HX1Z.Y1Z 1Y #Y  Y  Y  Y  :Y  Y  NX  Y 5Y/Z IX0X IY/Z KZ/Y KY/Z KY/Z KZ/Y 7\\ 7ZKW"
5284       ";Y IX1Y GX1Y GY2Y GY2Z <YIY <Z0Y ?YIZ     0X<X<X)X Y(XJY>YJX(XJY/X)X Y <X 'ZIZ     3~d FYKT   /gJQJg(nMRLm Hp 6"
5285       "m ?X.Y HY1X CZ<Y 6YKZ DY                   \"Z        1W?W %TAY @X>W;W7Y/c   2Y  EY    IX        5X /Z3Z 2Z .\\"
5286       "  3Z M~Z &Z FY1Z 8[ 6Z/Z $Z      i L~d Li   @W>Y7YBW0Z*Y NZ-[![ %Z'[ MZ  MZ -[$Z'Z(Z :Z \"Z 4Z<] <Z 2Y<]<Y(Y6ZL"
5287       "Z'[%[ MZ ,[%[#Z1[  N[ 8Y 8Z+[ AZJZ GZKW6WKZ NZ<[ 1Z 3[  EX  CX  KY        NY2Z FZ0Y IZ %Z1[ IY  =Y 4Z1Z GY1Y 5Y"
5288       "  NX 0XE\\ &Y FY3Y2Y+Y1Y JZ0Z IZ0Y MZ1Z ;Y  6Y 8Y .Y2Z =YGY AYKW?WKX B[J[ 1YJY 2[  GY  NX  Y     $ZL[H[JY#Y6\\ "
5289       "BY0Y GX8X BZ0Z GY #Y1Y ;YKZ 7Z:Y EY2Z%Z:Z:Z*ZBYBZ HY9Y IY@X@Y L[1Z MY/Y 3Y 4Z3Y LZ+Z C\\ 'Z +[,[!_GZG_#Z(Z Fq H"
5290       "[%[$\\  :X  ;\\ H\\JXJ\\ H\\1\\ J\\IXJ\\   (Y 3Z@U -Z &x 0Z  X  c   <UAmDV   =[CZ  AX  !VDW<YHU     (X        E"
5291       "Y1Y 1WEW          JZCZ 3W8UAV\"X*X LX9UAW G[ 9Z*Y!Z+Z Y*Y!Z+Z Y*Z\"Z+Z0Z3Z 8[  MZ !Z  Z !Z  >Z !Z !Z \"Z :Z(\\%"
5292       "Y6ZKY&[%[)\\&[)[%[)[%[)[%[ L\\>[ Ld.[&Z*Z!Z*Z\"Z+[\"Z+Z 8Z 2s <Y-Y NX1Z IY1Z IY2Z GY2Z HY2Z HX1Z.Y1Z 1Z $Y  Y  "
5293       "Z !Z  ;Y  Y  NX  Y 5Y/Z IX0X IY/Y JZ0Z KZ0Z KY/Y IY0Z 7\\ 6YLX<Z IX1Y GY2Y GY2Y GY2Z <YJZ <Z0Y >YJY     .X=X=Y("
5294       "X!X'YJW<WJX&XJW/Y(X!X ;X &YIY     #[  LYLU   .fJQJf&lLRLm Gn 4k >X.Y HY2Y CZ<Z 7YKY BY                   #[    "
5295       "    3X@X %TAY @W=W;W7Z0b   1Y  EY    IX        5X /Z3Z 2Z /\\  2Z )Z  JZ FZ2Z 8Z 5Z/Z %Z      Ki   :j   >W=X8ZC"
5296       "W/Z*Z Z-Z N[ &Z(\\ MZ  MZ -\\%Z'Z(Z :Z \"Z 4Z;] =Z 2Y<]<Y(Y6ZLZ&[&[ MZ ,\\'[\"Z0Z  NZ 7Y 8Z+Z @ZJY FZLX6XLY N[;"
5297       "Z 1Z 4\\  EX  BX  LY        NY2Z F[2Z HZ %Y1[ IZ  >Y 4Z2[ GY1Y 5Y  NX 0XD\\ 'Y FY3Y2Y+Y1Y IY0Z IZ1Z MZ1Z ;Y  6Y"
5298       " 8Y .Y2Z =ZIZ @XLX?WLY C[H[ 2YKZ 3[  EX  NX  Y     $hFh\"Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y ;YKY 6Y9Y EY2Z%Z;[:Z*ZBYB"
5299       "Y GY9Y IY@XAZ L[1Y LZ1Z 3Y 3Y3Y LZ*Z D[ &Z *[-[ aJZJa\"Z(Z Cl F\\'[\"\\  ;X  <\\ F\\KXK\\ F\\3\\ H\\JXK\\   'Y "
5300       "2ZAU -Z 'z 1Z  X  Na   ;V@jDV   :ZCZ  BX   UDW;XIU     'X        EY2Z 1WEW          KZCZ 3X9U@V\"W*X LX9VAW H[ "
5301       "8Z*Z\"Y)Y!Z*Z\"Z*Y!Z*Z\"Z*Z1Z3Z 8[  MZ !Z  Z !Z  >Z !Z !Z \"Z :Z(\\%Y5ZLY&[&['[&[([&[)\\'\\)[&[ L\\<[ Mc.[$Z,[!"
5302       "[,[\"Z+Z!Z+Z 8Z 2n 7Y-Y NX1Z IY2[ IY2Z GY2Z HY2Z IY2[.Y2\\ 2Z $Z !Z !Z !Z  ;Y  Y  NX  Y 5Z0Y HX0X IZ1Z IY0Z KZ0"
5303       "Y JZ1Z IZ1Z 7\\ 6YMX;Z IY3Z GY2Y GY2Y GY2Z ;YKY ;Z1Z >YJY     .Y>X=X'Y#Y&XIU:UJY&YJU.X'Y#Y ;X &YJZ     #Z  JXLU"
5304       "   -dIQId%kKRKk El 2j >X.Y HY2Y CY;Z 7ZMZ BZ                   #Z        3X@X %TAX @W<W;W7Z/a   0Y  FY    IX   "
5305       "     6X -Z4Z 2Z 0\\  2[ )Z  JZ FZ2Z 8Z 5Z/Z %Z      Hi   @j   :V=Y9ZDX/Z*Z Z-Z N\\ 'Z)\\ LZ  MZ ,[%Z'Z(Z :Z \"Z"
5306       " 4Z:] >Z 2Y;[;Y(Y5ZMZ&\\([ LZ +['[\"Z0[  Z 7Y 8[,Z ?YKZ EYLX6XLY [:[ 2Z 5\\  DX  BX  LY        NY3[ F[2Z HZ %Y1"
5307       "[ IZ  >Y 3Y2[ GY1Y 5Y  NX 0XC\\ (Y FY3Y2Y+Y1Y IZ2Z H[2Z LY1Z ;Y  6Z 9Y .Y2Z <YIY ?YMX?XMY CZFZ 2YKY 3[  DY  X  "
5308       "Y     #gEf!Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y :XLZ 7Z9Z FY2Z%Z;\\<[)ZCYCZ GY9Y IZAXAZ L[1Y LZ1Z 3Y 3Y4Y KZ*Z E[ %Z )["
5309       "/[ MdNZNd!Z(Z Ag B['[!\\  <X  =\\ D\\LXL\\ D[4\\ F\\KXL\\   &Z 3ZAU -Z (| 2Z  X  L^   9V?fBU   8ZCZ  CX   V JV "
5310       "             CY2Z 1WEW          LZCZ 2W9V@V#X+X KW8U@W I[ 7Z*Z#Z)Z\"Z)Y#Z)Z\"Z)Y#Z)Z2Z2Z 7[  NZ !Z  Z !Z  >Z !Z"
5311       " !Z \"Z :Z)\\$Y5ZLY%[(\\'\\(['\\(['['['[(\\ M\\:[ Ma-[$Z,Z NZ,Z![,Z!Z,[ 8Z 2Z #Y-Y NX2[ IY2[ IY2Z GY3[ HX2[ IY2"
5312       "[.Y2\\ 2Z $Z !Z !Z  Y  ;Y  Y  NX  Y 5Z1Z HX0X IZ1Z IZ1Z JZ2Z JZ1Z IZ1Z 7\\ 6c:Z IY3Z GY3Z GY3Z GY3[ ;YKY ;[2Z ="
5313       "YLY     ,Y?X>Y&Y%Y%YIS8SJY$YJS.Y&Y%Y :X &ZKY     #Z  IYNU   ,cISIb#jKRJi Cj 1i =X.Y GY4Y BY:Y 7ZMZ AZ          "
5314       "         $[,P       )W?X %TBY AX<W;W7[/_   /Y  FY    IX        6X -Z5Z 1Z 1\\  1Z (Z  K[ EY2Z 9Z 4Z0[ &[      F"
5315       "j   Ei   7W=Y;[EX/Z(Z!Z.[ M[!P'Z*] LZ  MZ ,\\&Z'Z(Z :Z \"Z 4Z9] ?Z 2Y;[;Y(Y4YMZ%[)\\ LZ +\\)[!Z/Z  Z 7Y 7Z-[ ?Z"
5316       "LZ EZMX6XMZ Z8[ 3Z 6\\  CX  BX  LY        NY3[ F[2Y GZ %Z3\\ HZ  ?Y 3Z4\\ GY1Y 5Y  NX 0XB\\ )Y FY3Y2Y+Y1Y IZ2Z "
5317       "H[2Y KZ3[ ;Y  6Z 9Y .Z4[ <YIY ?YMW>XMY DZDZ 2YLY 3[  DY  X  Y     \"eCd NY8^ CY0Y GX:Y @Z2Z FX \"Y1Y :YMY 6Y7Y "
5318       "FY2Z%[<\\<Z(ZCYCZ GY9Y HYAXAY K\\3Z KZ2Z 3Y 3Z5Y LZ)Z F[ $Z ([1[ K~U Z(Z ;[ <\\)[ N[  <X  <Z B\\MXM\\ BZ3Z D\\L"
5319       "XM\\   %Z 3ZAU -Z )~ 3Z  X  J]   9V>a@V   7YBY  CX   NV LV              BZ3Z 1WEW          LYBY 2W8U?V#W+X KX9U"
5320       "?W J[ 7Z(Y#Z)Z#Z(Z$Z)Z\"Y(Z$Z(Y2Z2Z 7\\\"P NZ !Z  Z !Z  >Z !Z !Z \"Z :Z*\\#Y4ZMY%\\)[%[)\\&[)\\'\\)\\'\\)[ M\\8"
5321       "[ N`-[#Z,Z NZ,Z Z-[![-[ 8Z 2Z #Y-Y NX2[ IY2[ IY3[ GY3[ HY3[ HX2[.Y3^ 2Z $Z !Z !Z !Z  <Y  Y  NX  Y 4Z2Z HX0X HZ2"
5322       "Z IZ2Z IZ2Z IZ2Z IZ2Z 6\\ 5a:Z HY3Z GY3Z GY3Z GY3[ ;YLY :[2Y <YLY     ,Y?X?Y$Y'Y#YIQ6QIY$YIQ.Y$Y'Y 9X %YLZ     "
5323       "$Z  HYNU   +aHSH`!hJRIg Bi /g <X.Y GY4Y CZ:Y 6YMY @[                   $Z-Q       )W?W $TBY AW;W<X6Z.]   .Y  GY"
5324       "    HX        6X -Z5Z 1Z 2\\  0Z (Z  L[ DZ4Z 8Z 4[1Z %Z      Bj   Ki   4W=Z=\\GY.Z(Z!Z.[ M\\#Q'Z+] KZ  MZ +\\'Z"
5325       "'Z(Z :Z \"Z 4Z8] @Z 2Y:Y:Y(Y4ZNZ%\\*[ KZ *\\+\\!Z/[ \"[ 7Y 7Z-Z >ZMZ DZMW4WMZ![7Z 3Z 7\\  BX  AX  MY        NY3"
5326       "[ F\\4Z FZ &Z3\\ HZ  ?Y 3Z4\\ GY1Y 5Y  NX 0X@[ *Y FY3Y2Y+Y1Y HZ3Z H\\4Z KZ3[ ;Y  5Y 9Y -Y4[ ;YKY >YNX=WNY D[D[ "
5327       "3YMY 3[  CY  X  Y     !cAb MZ9^ CZ2Z GX:Y @Z3Z EX \"Y1Y :YMY 7Z7Y FZ4[$Z<\\<Z(ZCYCY FY9Y HYAXBZ K\\3Z KZ3Z 2Y 2"
5328       "Y6Z LZ(Z H\\ $Z (\\3[ I~R MZ(Z :Z ;\\+\\ MY  ;X  ;X @\\NXN\\ @X1X B\\MXN\\   $Z 2ZBU -Z *~Q 4Z  X  I]   :W9U;V "
5329       "  5XAX  CX   MV NV              AZ3Z 1WEW          LXAX 2X8s+W,Y JW8t#\\ 7Z(Z%Z'Y#Z(Z$Y'Z$Z(Z$Z(Z4Z1Z 6[#Q NZ !"
5330       "Z  Z !Z  >Z !Z !Z \"Z :Z+]#Y4ZMY$[*\\%\\*[%\\+\\%\\+\\%\\+\\ N\\6[ N^-\\#[.[ N[.[ [.Z NZ-Z 7Z 2Z #Y-Y NY4\\ IY3"
5331       "\\ IY3[ GY3[ HY4\\ HX3\\.Y3^ 2Z $Z !Z !Z !Z  <Y  Y  NX  Y 4Z3Z GX0X HZ3Z GZ3Z IZ3[ IZ3Z GZ3Z 6\\ 5`9Z HY4[ GY4["
5332       " GZ5[ GZ5\\ :YMY :\\4Z ;XMZ     +Y@X@Y#Z)Z\"Y(Y\"Y(Y#Z)Z 9X %ZMZ     %Z  F_   )^GSG^ NfIRHe @g -e ;X.Y GZ6Z CY9"
5333       "Z 7ZNY ?[                   %[/R       *X@X $TBY BX;X=X6[.]   /Y  GY    HX        7X +Z7Z 0Z 3\\  0[ (Z  L[ DZ4"
5334       "Z 9[ 3Z2[ &Z      >i   i   2W<Z?]HZ.Y'Z!Z/\\ L\\&S'Z,] JZ  MZ *\\(Z'Z(Z :Z \"Z 4Z7] AZ 2Y JY(Y3e$\\,\\ KZ )\\-"
5335       "\\ Z.Z \"[ 7Y 7[/[ =ZNZ DZNX4XNY![6[ 4Z 7[  AX  AX  MY        NY4\\ F\\4Z F[ &Z5] H[  @Y 2Z6] GY1Y 5Y  NX 0X?[ +"
5336       "Y FY3Y2Y+Y1Y HZ4Z G\\4Z JZ5\\ ;Y  6Y 8Y -Y5\\ ;YKY =XNX=WNY E[B[ 3YNY 4[  BY  X  Y      N_=_ LZ:_ CZ2Y FX;Y >Z4"
5337       "Z EY #Y1Y 9XNZ 7Y6Z GZ4[$Z=]=['ZDYDZ FY9Y HZBXBZ K]5Z J[5[ 2Y 2Z7Y L[(Z H[ #Z '\\5[ F~ LZ(Z :Z :\\-\\ KW  :X  :"
5338       "V >r >V/V @s   #Z 2[CU -Z +[MeL[ 5Z  X  G\\   :W!V   3W@W     7V!W              AZ4[ 1WEW          LW@W 1W7s,X-"
5339       "Y JX8t$\\ 7Z'Z%Z'Z$Z'Y%Z'Z$Z'Y%Z'Z4Z1Z 6\\&S NZ !Z  Z !Z  >Z !Z !Z \"Z :Z,]\"Y3ZNY$\\,\\#\\,\\$\\,\\$\\-\\$\\,"
5340       "\\ N\\4[ ]-\\![/Z LZ/[ N[/[ N[/[ 7Z 2Z #Y-Y NY4\\ HY5] IY4\\ GY4\\ HY4\\ HY4\\.Z5` 2Z $Z !Z !Z !Z  =Y  Y  NX  Y "
5341       "3Z4Z GX0X H[5[ GZ4Z GZ4Z H[5[ GZ4[ 6\\ 5_9[ HZ5[ GZ5[ FY5[ FY5\\ :YNZ :\\4Z ;YNY     )YAXAZ\"Z+Z!Z*Y Y*Z\"Z+Z 8"
5342       "X $YMY     %[  F^   '\\FSF\\ LcGRGc >f ,c :X.Y FZ7Y BY8Y 7e >[                   %[1S   -Y   'X@X ;Q:TCZ CX:X=X"
5343       "5[.]   /Y  HY    HX  NZ    GZ 'X +[8Z 0Z 4\\  0[ 'Z  M\\ CZ6[ 9Z 2[3[ '[ 0Y  Y  ?f   f  BX DW=\\C_J[.Z&Z\"Z0\\ "
5344       "J\\(T'Z._ JZ  MZ *])Z'Z(Z :Z \"Z 4Z6] BZ 2Y JY(Y3e#\\.\\ JZ )]/\\ NZ.[ NQ'[ 6Y 6[0[ =ZNZ CYNX4XNY!Z4[ 5Z 8[  @X"
5345       "  AX  MY        NY5] F]6Z DZ &Z5] G[  AY 2[8^ GY1Y 5Y  NX 0X>[ ,Y FY3Y2Y+Y1Y H[6[ G]6Z IZ5\\ ;Y  6Y 8Y -Z6\\ ;Z"
5346       "MZ =b=b EZ@Z 3d 5[  AY  X  Y      L[:\\ IZ;` D[4Z FX<Z >Z5[ EY #Y1Y 9c 7Z5Y GZ5\\$[>^>['[EYE[ FY9Y HZBXCZ J]5Z "
5347       "IZ5Z 1Y 1Y8Z LZ&Z J[ \"Z &\\8] E| KZ(Z :Z :]/] JU  9X  9T <p <T-T >q   \"Z 1ZCU -Z ,[JaI[ 6Z  X  F\\   :W#V   1"
5348       "V?V     7W#W              @[5[ 1WEW          LV?V 1X7s,W-Y JX7t%\\ 6Z&Z&Z'Z%Z&Z&Z'Z%Z&Z&Z&Y4Y0Z 5\\(T NZ !Z  Z "
5349       "!Z  >Z !Z !Z \"Z :Z.^!Y3e#\\.\\!\\.\\#].\\#]/]#\\.\\ N\\2[ ]/]![0[ L[0[ M[0[ N\\1[ 6Z 2Z #Y-Y NY5] HY5] IZ6] GY"
5350       "5] HY5] HY5]-Y5a 3[ %[ \"[ \"[ \"[  >Y  Y  NX  Y 3Z5[ GX0X GZ5Z F[6[ G[6[ GZ5Z F[5Z 5\\ 4^9Z FY6\\ FY6\\ FY6\\ "
5351       "FY6] 9c 9]6Z :d     )[CXBZ Z-Z NZ-[ [-Z Z-Z 7X $YNZ     %Z  D]   $VCSDW G`FSG` ;d +c :X.Y F[9Z CZ8Y 6d =\\     "
5352       "              '\\3T   -Z   (W?X ;S<TDZ BW8W=W4\\1`   0Y  HY    HX  NZ    GZ 'X *Z9Z /Z 5\\  0\\ 'Z  N\\ B[8[ 8Z"
5353       " 2\\5[ '[ /Z \"[  >d   c  @Z EW<_Ks-Z&Z\"Z1] J^,V'Z/_ IZ  MZ )]*Z'Z(Z :Z \"Z 4Z5] CZ 2Y JY(Y2d#]0\\ IZ (]1] NZ-"
5354       "Z NS*\\ 6Y 6[1[ <e Bc4c\"[3Z 5Z 9\\  @X  AX  MY        NZ6] F^8[ D[ &Z7^ G[  AY 1[:_ GY1Y 5Y  NX 0X=[ -Y FY3Y2Y"
5355       "+Y1Y G[7Z F]7[ HZ7] ;Y  6Y 7Y .Z7] :YMY <a<a EZ>Z 4c 5[  @Y  X  Y      HS3V FZ<a D\\5Z FX<Y =[7[ DZ $Y1Y 9c 7Y4"
5356       "Z H[6\\#Z?WNV>Z%ZEYF[ EY9Y GZCXD[ J^7Z H[7[ 1Y 1Z:Z KZ&Z K[ !Z %];] Bx IZ(Z :Z 9]1] HS  8X  8R :n :R+R <o   !Z "
5357       "1[DU -Z -[F\\F[ 7Z  X  E\\   :W&W   /U>U     6W%W              ?[6\\ 1WEW          LU>U 0W6s-X.X HW6t&\\ 5Z&Z'Z"
5358       "%Z&Z&Z'Z%Z&Z&Z&Z&Z6Z0Z 4],V NZ !Z  Z !Z  >Z !Z !Z \"Z :Z0`!Y2d\"\\0]!]0\\!]0\\!]1]!]1] \\0[ ]1] N[2\\ L\\2[ L\\"
5359       "2[ L[1[ 6Z 2Z #Y.Y MZ7^ HY6^ HY6] GZ6] HZ7^ HZ7^-Y6c 3[ %[ \"[ \"[ \"[  ?Y  Y  NX  Y 3[7[ FX0X G[7[ E[7[ FZ7[ F"
5360       "[7[ E[7[ 5\\ 4]9[ FZ8] FZ8] FZ8] FZ7] 9c 9]7[ 9b     '[DXD[ N[/Z LZ/[ M[0[ N[/Z 6X $d     %Z  C\\    ?S 2\\ETD"
5361       "\\ 9b )a 9X.Y E[<[ BY7Z 7c ;\\                   '\\5U   -Z   (W?W :U>TE[ CX8X?X3\\3b   1Y  IY    GX  NZ    GZ ("
5362       "X )[;[ /Z 5[ %Q-\\ &Z BQ/] AZ9\\ 9Z 0[6\\ (\\ /Z \"[  ;a   `  =Z EX<nNd,Z$Y\"Z2] H^.W'Z2a HZ  MZ (^,Z'Z(Z :Z \""
5363       "Z 4Z4] DZ 2Y JY(Y2d\"]3^ IZ ']3] MZ-[ U-] 6Y 5\\4\\ ;d Bb2b#[2[ 6Z :\\  ?X  @X  NY        MZ8^ F^8Z B[ '[9_ F[,"
5364       "P 7Y 1\\<` GY1Y 5Y  NX 0X<[ .Y FY3Y2Y+Y1Y G[8[ F^9[ G[9^ ;Y *Q/Z 7Y -Z9^ :YMY <a;` F[>[ 4b 6[  ?Y  X  Y        "
5365       "FZ=b E]7Z EX=Z <[9\\ D[ %Y1Y 8a 6Y3Y H\\8]#[@WNW@[%[FYG\\ EY9Y G[DXD[ J_9[ G[9[ /Y 1Z;Z LZ%Z L\\ !Z $]=\\ >t GZ"
5366       "(Z :Z 8]3] FQ  7X  7P 8l 8P)P :m    Z 0[EU -Z .[?P?[ 8Z  X  D[   9W(W   -T<S     5X)X              >\\8] 1WEW  "
5367       "        LS<T 0W5s-W.X HX6t'\\ 5Z$Y'Z%Z'[%Z(Z%Z&Z%Z(Z%Z6Z0Z 4^.W NZ !Z  Z !Z  >Z !Z !Z \"Z :Z2a Y2d\"^3] N]3^ ]3"
5368       "] N]3] N]3] \\.[!^3] M\\4\\ J\\4\\ K\\4\\ L\\4\\ 5Z 2Z #Y.Y MZ8_ HZ8_ HZ8^ FZ8^ HZ8_ HZ8_-Z8e-Q)\\ &\\-Q G\\-Q "
5369       "G\\-Q G\\-Q 5Y  Y  NX  Y 2[9\\ FX0X F[9[ D\\9[ E[8[ E[9[ D\\9[ 4\\ 3[9[ EZ9^ FZ9^ FZ9^ F[9^ 9b 8^9[ 8b     &[2["
5370       " L\\3\\ K[2[ K[2[ L\\3\\ 6X #c     &Z  B\\    ?S /UATAT 4a '_ 8X.Y E\\>\\ BY6Y 7c :]                   (\\7V   "
5371       "-Z   )X@X :W@TF[ BW7X?X3]6e   1X  IY    GX  NZ    GZ (X ([=[ .Z 6[ $S1^ &Z BS3^ @\\<\\ 8Z 0]9] FR6] .Z \"[  8^ "
5372       "  ^  ;Z DW;lMc+Z$Z#Z4_ G_2Y'Z5c GZ  MZ '^/\\'Z(Z :Z \"Z 4Z3] EZ 2Y JY(Y1c!^6^ HZ '^6^ LZ,Z X1] 5Y 5]6\\ :c Ab2a"
5373       "\"Z0[ 7Z ;\\  >X  @X  NY        MZ:` F_:[ B\\3P D[;` E\\1S 7Y 0\\>a GY1Y 5Y  NX 0X;\\ 0Y FY3Y2Y+Y1Y F[:[ E_;\\ "
5374       "F[;_ ;Y *S1Y 6Z .[;_ :e ;`;` G[<[ 5a 6[  >Y  X  Y        F[?YNY F_:[ DX?Z :[;\\ B[ &Y1Y 8a 7Z3Y H]:^#\\BXNWA[#["
5375       "GYH\\ DY9Y F\\FXF\\ I`;[ F\\;\\ /Z 2[=Z KZ$Z N\\  Z #^A] :n DZ(Z :Z 7]5]   +X    Mj   (k    NZ 0\\FUBP ;Z /[,[ "
5376       "9Z  X  CZ   8X+W   *R;R     4X+X              =]:^ 1WEW          LR;R /X5s.W.X GW5t(\\ 4Z$Z(Z%Z'Z$Z(Z$Y'Z$Z(Z$Z"
5377       "8Z/Z 3_2Y NZ !Z  Z !Z  >Z !Z !Z \"Z :Z5c NY1c!^6^ L^6^ M^6^ M]5] M^6^ \\,[#a7^ K\\6] I\\6\\ J]6\\ J\\6] 5Z 2Z #"
5378       "Y/Z LZ:` H[:` H[:_ FZ:` GZ:` GZ:`-[:YN\\0S(\\4Q C\\0S F\\0S F\\0S F\\0S 5Y  Y  NX  Y 1[:[ EX0X F\\;\\ C\\;[ C[:"
5379       "[ D\\;\\ C\\;\\ 4\\ 3[:\\ DZ;_ EZ;_ EZ;_ EZ;` 8a 8_;\\ 7a     %\\6\\ J\\5\\ I\\6\\ I\\6\\ J\\5\\ 5X #c     'Z  "
5380       "@[    @T  JT  _ %] 7X.Y D^D^ BZ6Y 6b 9_                   *];X   -Z   )X@X :ZCTH] CX7YAX1^:h   2Y  JY    GX  NZ"
5381       "    GZ (X (\\?\\ .Z 7\\ $W7_ %Z BV8` ?\\>] 9[ /];] ET9] -Z \"[  5[   [  8Z DX;jLb*Z$Z#Z7a E`7\\'Z9f FZ  MZ &`4^"
5382       "'Z(Z :Z \"Z 4Z2] FZ 2Y JY(Y1c _:_ GZ &_9^ KZ,[![6^ 4Y 4]9] 8b @a2a#[/Z 7Z ;[  =X  @X  NY        M[<a Fa>\\ @]7R"
5383       " D\\=a E]4U 7Y /]Bc GY1Y 5Y  NX 0X:\\ 1Y FY3Y2Y+Y1Y E\\>] E`=\\ E\\=` ;Y *U5[ 6[ /\\>a 9c :_:` GZ:Z 4` 6[  >Y  "
5384       "X  Y        E[AYMZ G`<[ CX@Z 9\\=\\ A\\3Q EY1Y 7` 7Y2Z I^<_\"[BWMXC\\#]IYI\\ CY9Y F]GXG] Ia=\\ E\\=\\ .[ 2[?Z J"
5385       "Z$Z N[  NZ \"^C^ 7g @Z(Z :Z 7_9_   +X    Lh   &i    MZ /]HUDR ;Z .Y*Y 8Z  X  BZ   8Y/X   (Q:Q     2X/Y         "
5386       "     <^<` 2WEW          LQ:Q .W MV(X/X GX NW\"\\ 3Z$Z)Z#Z(Z$Z)Z#Z(Z$Z)Z#Z8Z/Z 2`7\\ NZ !Z  Z !Z  >Z !Z !Z \"Z :"
5387       "Z9f MY0b _:_ J_:_ K_:_ L_9_ L_9^ N[*[$c:^ J^:^ H^:^ I^:] H]9] 4Z 2Z #YIP7[ L[<a G[<a G[=a F[<a G[<a G[<a,[=ZL\\"
5388       "4V'\\7S B\\4V E]5V E]5V E]5V 5Y  Y  NX  Y 1\\=\\ DX0X E\\=\\ A\\=\\ C]>] C\\=\\ A\\=\\ 3\\ 2\\=\\ C[=` E[=` E[="
5389       "` E[=a 8a 8`=\\ 6`     #]:] H]9] G]:] G]:] H]9] 4W !a     'Z  ?Z    ?U  KT  N] $] 7X.Y Cv AZ6Z 7a 7a           "
5390       "        -_?Z   -Z   )W?X :^GTK_ CX5XAX0_>k   3Y  JX    FX  NZ    GZ )Y ']C] ?} I~S IZ=b %Z BZ>a =]B^ 8Z ._?^ DX"
5391       "@_ ,Z \"[  3Y   X  5Z CW:gJ`)Z\"Z$~T Cb=_'~W E~S FZ %b:a'Z(Z :Z \"Z 4Z1] G~Q)Y JY(Y0b N`>` FZ %a?` JZ+Z!^<a 4Y "
5392       "3_>_ 8b @a2a$[.[ 8Z <~` AX  ?X  Y        L\\@c Fb@] ?^<U C]Ac D^9X 7Y /aI[NY GY1Y 5Y  NX 0X9\\ 2Y FY3Y2Y+Y1Y E]"
5393       "@] Db@\\ C]Ab ;Y *X9\\ 5] 1\\Ac 9c :_:_ GZ9[ 5` 7[  =Y  X  Y        E]DZM[ Hb@] BXB[ 8]A^ @]8T EY1Y 7_ 7Z1Y I`@"
5394       "b#]EXLXE\\!]JYK^ CY9Y E_JXJ_ HcA] C]A] ,] 4[B\\ K~c!~T FZ 3oDo A[ :Z(Z :Z 6a?a   *X    Kf   $g    LZ .^JUGU H~U"
5395       " JW(W 7Z  X  AY   7Z3Y   &P9P     1Y3Y     <~d       3`@b 2WEW          LP9P .X MV(W/X GX MW#\\ 3Z\"Z*Z#Z)Z\"Z*"
5396       "Z#Z)[#Z*Z#Z9Z.~T+b=_ N~T J~T I~S I~S 7Z !Z !Z \"Z :~V KY0b N`>` H`>` I`>` Ja?a Ja?` LY(Y$f?` H_>_ F_>_ G_>_ H_>"
5397       "_ 3Z 2Z #YIS;[ K\\?c G\\?c G\\?b E\\@c F\\@c G\\?c,\\?[L^9Y'^<V B_:Y E_:Y E_:Y D^:Y 5Y  Y  NX  Y 0]@] DX0X D]A]"
5398       " @]@] A]@] A]A^ A]@] 2\\ 2]@] B]Ab E]Ab D\\Ab D\\Ac 7_ 7b@\\ 5`     \"_@_ F_?_ E_@_ E_@_ F_?_ 3W !a     'Z  ?Z "
5399       "   ?U  KT  M\\ #[ 6X.Y Bu AY5Z 7a 6f                   2aE]   -Z   )W?W 9~ BW4YCY/bFp   3X  KY    FX  NZ    GZ "
5400       ")X %^G^ >} I~S I~ $Z B| ;^F_ 7Z -aEa Dv +Z \"[  0V   U  2Z CX9dI^'Z\"Z$~S AfGd'~U C~S FZ $gGg&Z(Z :Z \"Z 4Z0] H"
5401       "~Q)Y JY(Y0b McGd EZ $dGc IZ+[\"cEd 3Y 3cGc 7a ?`1a$Z,[ 9Z =~a AX  ?X  Y        L^DZNY FYNZF_ =`CY B^EZNY CaB] 7"
5402       "Y .qMY GY1Y 5Y  NX 0X8\\ 3Y FY3Y2Y+Y1Y D_F_ CYNYE_ B^EZNX ;Y *]A^ 4k >^G[NY 8a 9_9^ H[8[ 5^ 6~P 2Y  X  Y       "
5403       " D^H[La NfH` AYD[ 6^E_ ?`?X EY1Y 7_ 7Y0Y IcFk(]HZLZI^ `Nk BY9Z E~Q GYNZE^ B_E_ ,e ;]G] J~c!~T FZ 3oDo @Z :Z(Z :"
5404       "Z 5dGd   )X    Jd   \"e    KZ -`MUKY H~U IU&U 6Z  X  AY   5Z7Z          LZ7Z     ;~d       3cFk 8WEW           "
5405       " BW LV)X0X FW LW$\\ 2Z\"Z+[#Z)Z\"Z*Z\"Z*Z\"Z*Z\"Z:Z.~T*fGd N~T J~T I~S I~S 7Z !Z !Z \"Z :~U JY/a MdGc FcGd GcGd"
5406       " HdGd HdGc JW&W$kGc FbFb DbFb FcFb FcGc 3Z 2Z #YIWB] I^DZNY F]D[NY F]D[NX E^DZNY F^DZNY F^E[NY+]D]J`@]&`BY AaA]"
5407       " DaA] DaA] DaA] 5Y  Y  NX  Y /_F_ CX0X D_E_ ?_F_ ?_F_ @_E_ ?_F_   7aF_ @^FZMX D^FZMX D_GZMX D_G[NY 7_ 7YNYE_ 4^"
5408       "      dLd CdMd BdLd CdLd DeMd 2X !`     %X  =Y    ?U  LV  MZ !Y 5X.Y As AZ4Y 6` 5~]                  )x   -Z   "
5409       "*X@X 9} BX3YFZ-{L]   4Y  LY    FX  NZ    GZ )X $t >} I~S I} #Z B{ :v 7[ ,{ Cu *Z \"[  -S   S  0Z BW8aG[%[\"Z$~R"
5410       " ?~S'~T B~S FZ #~V%Z(Z :Z \"Z 4Z/] I~Q)Y JY(Y/a L~ DZ #~ HZ*Z\"~R 2Y 2} 5` ?`0_$[+Z 9Z =~a AX  ?X  Y        KsN"
5411       "Y FYNr ;u AqMY B{ 7Y -oLY GY1Y 5Y  NX 0X7\\ 4Y FY3Y2Y+Y1Y Cv BYNr ArMX ;Y *y 2j >qMY 8a 8^9^ I[6Z 5^ 6~P 2Y  X "
5412       " Y        CpK` N} ?YF[ 5w =x EY1Y 6] 7Z0Z J~Y(nJm M{ AY9\\ F~ FYMq @w *d ;r J~d!~T FZ 3oDo @Z :Z(Z :Z 4~   'X  "
5413       "  Ib    c    JZ ,u H~U HS$S 5Z  X  AY   4\\>\\          I]>\\     :~d       3~Y 8WEW            CW KV)W0X FX LW"
5414       "$[ 2[\"Z+Z!Z*Z\"Z+Z!Z*Z!Z,Z!Z:Z.~T)~S N~T J~T I~S I~S 7Z !Z !Z \"Z :~T IY/a L~ D~ E~ F~ E~ HU$U$~X D| B| D} D} "
5415       "2Z 2Z #YIr HrMY FsMY FsMX DsNY ErMY FsMY+uH|%v @| C| C| C| 5Y  Y  NX  Y .v BX0X Cw =w >v >w =w   8{ ?qMX CqMX C"
5416       "qMX CqMY 6] 6YNr 3^      My Ay @y @z Ay 1X  _     $V  <X    ?V  LV  LX  NW 4X.Y @p ?Z4Z 7_ 2~[                 "
5417       " (v   ,Z   *X@X 9| AW1[K[+yJ]   5Y  LX    EX  NZ    GZ )X #r =} I~S I| \"Z Bz 8t 6Z *y Bt )Z \"[  *P   P  -Z BX"
5418       "6[DX\"Z Z%~Q <~P&~R @~S FZ \"~T$Z(Z :Z \"Z 4Z.] J~Q)Y JY(Y/a K| CZ !{ GZ*[#~Q 1Y 1{ 4_ =_0_%[*[ :Z =~a AX  >X !"
5419       "Y        JqMY FYMp 9t ApLY Az 7Y ,mKY GY1Y 5Y  NX 0X6\\ 5Y FY3Y2Y+Y1Y Bt AYMp ?pLX ;Y *x 1j =oLY 8a 8]8^ IZ4Z 6"
5420       "] 5~P 2Y  X  Y        CoI_ N} ?[K] 3u ;w EY1Y 6] 7Y.Y JvM_'mJm Ly @Y9b K| EYLp ?u (c :p I~e\"~T FZ 3oDo @Z :Z(Z"
5421       " :Z 2{   &X    H`    Ma    IZ +t H~U GQ\"Q 4Z  X  AY   2aLb          FaKa     8~d       3YNlN_ 8WEW            "
5422       "DX KV*W0o-W KW%[ 1Z Z,Z!Z+Z Z,Z!Z+Z Z,Z!Z;Z-~T'~P M~T J~T I~S I~S 7Z !Z !Z \"Z :~R GY.` K| B| C{ B{ B{ FS\"S$YM"
5423       "{ Bz @z B{ B{ 1Z 2Z #YIq GqLY EqLY EqLX CqMY ErMY EqLY*sF{$u ?{ B{ B{ B{ 5Y  Y  NX  Y -t AX0X Bu ;u <t <u ;u   "
5424       "8{ >pLX CpLX CpLX BoLY 6] 6YMp 1]      Lv >w =v =v >w 0X  _     #T  ;X    ?W  MV  LW  LV 4X.Y ?n >Y3Z 7_ 1~Z   "
5425       "               't   +Z   *W?X 8y @X1j)vG]   5X  MY    EX  NZ    GZ *X !p <} I~S Iz  Z By 6r 5Z )w As (Z \"[    "
5426       "    5Z AX  HZ Z%~ 9|$~P >~S FZ  ~P\"Z(Z :Z \"Z 4Z-] K~Q)Y JY(Y.` Jy AZ  x EZ)Z#~P 0Y /x 3_ =_0_%Z([ ;Z =~a AX  "
5427       ">X !Y        JpLY FYLn 7s @nKY @y 7Y +kJY GY1Y 5Y  NX 0X5\\ 6Y FY3Y2Y+Y1Y Ar @YLn =nKX ;Y *w /i <mKY 7_ 7]8] IZ"
5428       "3[ 6\\ 5~P 2Y  X  Y        BmH_ N{ <k 0r 9v EY1Y 6] 8Z.Z KYNkM_&kHk Jw ?Y8a Jy CYKn =s &b 9n H~e\"~T FZ 3oDo @Z"
5429       " :Z(Z :Z 1y   %X    G^    K_    HZ *s H~U   *Z  X  AY   1t          Bs     6~d       3YNkM_ 8WEW            DW "
5430       "JV+X0o.X KW%Z 0Z Z-Z NZ,Z Z-[ Z,Z Z-[ Z<Z-~T&| K~T J~T I~S I~S 7Z !Z !Z \"Z :~P EY.` Iy @y @y @y @y DQ Q$YKy @x"
5431       " >x ?x @y 0Z 2Z #YIp EoKY DoKY DoKX BoLY DpLY DoKY)qCy#t =y @y @y @y 5Y  Y  NX  Y ,r @X0X As 9s :r :s 9s   7z <"
5432       "nKX BnKX BnKX BnKY 6] 6YLn 0\\      Jt ;s :t ;t ;s .X  N]     !R  9V    >W  NX  LU  KU 3X.Y >l =Y2Y 7_ /~X     "
5433       "             %p   )Z   *W?W 4u @X/i(tE]   6Y  NX    DX  NZ    GZ *X  m :} I~S Iy  NZ Bw 2o 5Z 'u @r 'Z \"Z     "
5434       "   4Z AY  J[ Z%} 6x\"} <~S FZ  N| Z(Z :Z \"Z 4Z,] L~Q)Y JY(Y.` Hv @Z  Mu DZ)[$~ /Y .u 0^ =^/_&['Z ;Z =~a AX  >X"
5435       " !Y        InKY FYKl 5r ?lJY >w 7Y )hIY GY1Y 5Y  NX 0X4\\ 7Y FY3Y2Y+Y1Y @p ?YKl ;lJX ;Y *v -h ;kJY 7_ 7]7\\ J[2"
5436       "[ 7\\ 5~P 2Y  X  Y        AkE] Nz :i .p 7u EY1Y 5[ 7Y,Y KYMiL_%iGj Hu >Y8a Hv BYJl :p $a 7k H~f\"~T FZ 3oDo @Z "
5437       ":Z(Z :Z /u   #X    F\\    I]    GZ )r H~U   *Z  X  AY   /p          >o     4~d       3YMiK^ 8WEW            EX "
5438       "JV+W/o/X JW&Z 0[ Z-Z NZ-[ [.Z NZ,Z NZ.Z NZ=Z,~T$x I~T J~T I~S I~S 7Z !Z !Z \"Z :| BY-_ Hv <v =v =u =v   BXHu =v"
5439       " <v =u <u .Z 2Z #YIo CmJY CmJY CmJX BnKY CmJY CmJY(oAx!r <x ?x ?x ?x 5Y  Y  NX  Y +p ?X0X ?p 7p 7p 7p 7p   6WNp"
5440       " 9lJX AlJX AlJX AlJY 5[ 5YKl /\\      Hp 8q 7p 7p 8q -X  N]      NP  9V    ?Y  X  KS  IS 2X.Y <h <Z2Y 6^ -~V   "
5441       "               $n   (Z   +X@X 1o =W-f$pB]   6X  NX    DX  Z    FZ *X  Nk 9} I~S Iw  LZ Bv 0m 4Z %q >p %Z \"Z   "
5442       "     4Z @X  JZ MZ&{ 3u z 9~S FZ  Lx MZ(Z :Z \"Z 4Z+] M~Q)Y JY(Y-_ Fr >Z  Lr BZ(Z!y -Y -s /] <^.]&[&[ <Z =~a AX "
5443       " =X \"Y        GjIY FYJj 2p =iIY =u 6Y 'dGY GY1Y 5Y  NX 0X3\\ 8Y FY3Y2Y+Y1Y >m >YJj 8iIX ;Y *u *f :iIY 7_ 6\\7"
5444       "\\ K[0Z 6Z 4~P 2Y  X  Y        ?hC\\ NYMm 8f +m 3s EY1Y 5[ 8Z,Y KYLgJ^$gEh Fs =Y8a Fr @YIi 7m !` 6i G~g#~T FZ 3o"
5445       "Do @Z :Z(Z :Z .s   \"X    EZ    G[    FZ 'p H~U   *Z  X  AY   ,k          :k     2~d       3YLgJ^ 8WEW         "
5446       "   EW IV,X/o/W IW&Z 0Z MZ/[ NZ-Z MZ.Z N[.Z MZ.Z MZ>Z,~T\"t G~T J~T I~S I~S 7Z !Z !Z \"Z :y ?Y-_ Fr 8r 9r :s :r "
5447       "  AXEr :r 8r :s :s -Z 2Z #YIn AkIY BkIY BkIX @jIY BkIY BkIY'l=t Mq :t ;t ;t ;t 3Y  Y  NX  Y *m =X0X >m 3m 5n 5m"
5448       " 3m   6XLm 7iHX @iHX @jIX @jIY 5[ 5YJj -Z      El 3k 2l 3l 4l *X  N\\        5U    ?Y  Y  KR  HQ 1X.Y 9b 9Y1Z 7"
5449       "] )~S                  \"j   &Z   +X@X -h ;X+c!l?\\   6X  Y    DX  Z    FZ +X  Kh 8} I~S Fr  JZ As ,i 3[ $n ;m "
5450       "#Z \"Y        3Z ?X  KZ MZ&x -p Mu 4~S FZ  Js JZ(Z :Z \"Z 4Z*] N~Q)Y JY(Y-_ Dn <Z  Jn @Z([ Nt +Y +o ,\\ ;].]&Z$"
5451       "[ =Z =~a AX  =X \"Y        FhHY FYHf .m ;gHY ;p 3Y %`EY GY1Y 5Y  NX 0X2\\ 9Y FY3Y2Y+Y1Y =j <YHf 5gHX ;Y (q &d 9"
5452       "fGY 6] 5[6\\ KZ.Z 7Z 4~P 2Y  X  Y        >gB[ NYLj 5d (j 0q EY1Y 5Z 7Y+Z LYKdG]\"dBd Bo ;Y7` Dn >YHg 4i  L^ 4e "
5453       "E~g#~T FZ 3oDo @Z :Z(Z :Z ,n    NX    DX    EY    EZ %m G~U   *Z  X  BZ   )e          4e     /~d       3YKeH] 8"
5454       "WEW            FW HV,W.o0X IW'Z /Z MZ/Z LZ.Z MZ/[ MZ.Z MZ/[ MZ>Y+~T p E~T J~T I~S I~S 7Z !Z !Z \"Z :u ;Y,^ Dn 4"
5455       "n 5n 6o 6n   @XBm 5n 4n 6o 6o +Z 2Z #YIl =gGY AhGY AhGX ?hHY @hHY @gGY%i:o Hm 7p 6o 6p 7p 1Y  Y  NX  Y (i ;X0X "
5456       "<i 0j 1j 1j 1j   5XIi 3fGX >fGX >fGX >fGY 4Y 4YHf +Z      Bg /g .g -g /g (X  M[        5T    ?Z !Z  JP   'X.Y 5"
5457       "[ 6Y0Y 7] &~P                   Ne   $Z   +W?X '] 6W)a Mh<\\   7Y !X    CX  Y    EZ +X  Id 6} I~S Cm  HZ =l 'e "
5458       "1Z  i 6h !Z #Z        3Z ?Y  M[ M['s &k Jo .~S FZ  Gm GZ(Z :Z \"Z 4Z)] ~Q)Y JY(Y,^ Bi 9Z  Gl AZ'Z Jm (Y (i )\\ "
5459       ";].]'[#Z =Z =~a AX  =X \"Y        DdFY FYFb *h 6cFY 8j 0Y \"YAY GY1Y 5Y  NX 0X1\\ :Y FY3Y2Y+Y1Y ;f :YFb 1cFX ;Y"
5460       " $k  ` 7cFY 6] 5[5Z KZ-[ 8Y 3~P 2Y  X  Y        ;b=X NYJe 0` $e +l BY1Y 4Y 7Y*Y LYIaE[ b@a >k 9Y6_ Ah ;YFc 0e  "
5461       "FZ 2a D~i$~T FZ 3oDo @Z :Z(Z :Z )i    LX    CV    CW    DZ #h D~U   *Z  X -R9Z   #[          *[     *~d       3"
5462       "YIaE\\ 8WEW            GX HV-W-o0W HW'Z 0Z L[0Z LZ/[ LZ0Z LZ/[ LZ0Z LZ?Z+~T Lj B~T J~T I~S I~S 7Z !Z !Z \"Z :o "
5463       "5Y,^ Ai /h 0i 0i 0i   >W?i 1j 0j 1j 1i (Z 2Z #YGh 9cEY ?dEY ?dEX =dFY >dFY >cEY#d5j Ch 1j 1j 1j 1j -Y  Y  NX  Y"
5464       " &e 9X0X :e ,f -f -e ,f   4XFe 0cEX <bEX <bEX <bEY 4Y 4YFb )Z      ?` (a '` '` (a %X          'T               "
5465       "       L{                   K_          0T 4X&[ Ga    AX \"Y      :Y      EX  G_      Ie   #e !_    c /a    EY "
5466       "         EY       Hc        ?e      FZ          +b    Ni   )d    Nc            (X  =Y #Y        A^  J^ %a /]  N"
5467       "c    ;Y      NX          *` 7YD^ ,]CX   1c    ^        /Y    DY  X  Y        8] 1YF] *\\  N` %c  DY 4Y   *YG\\A"
5468       "X J\\;] 9e  A^ =` 7YC] *_    G[                 >a             NU    CZ  N`        9X -T<[                     "
5469       "         LYG]BX 5WEW   %U        HW  NX  MX  GZ                 (d                       +b (b )b )a )b   9V;a "
5470       ")c *c *c *c      =a 4_ &^ %^ $^ &_ &_ :_/c <b +c *c *c *c          3_    K_ &` '` '_ &`   1WB_ *] $] $^ %^  NZ "
5471       "4YD^ 'Y      6Q  HQ  GQ  FQ  HQ  LX          &S                                          DQ          )T 4W Q :Q"
5472       "    9Y #X      :Y      EX  ?Q      8R    ?R  @Q    @R  MQ    =Y          DY       @R        -Q      <Z         "
5473       " #R    >RM]   !R    <R             X  <X #Y        ;Q  <Q  GR !Q  @Q    2Y      MX          #R 0Y=Q  Q=X   'Q  "
5474       "  @Q        *Z    DY  X  Y          ;Y  <P  AQ  CQ  ;Y 4Y   *YAQ8P @Q0Q -Y  8X 7Y 3Y=Q  LQ                     "
5475       " JQ                 4Z  IU        3X -W@[                              KYAQ8P 1WEW   $U        IV  MW  LW  FZ  "
5476       "                V                        KQ  GR  HQ  GQ  GQ   0T2Q  HR  GR  HR  HQ      ,Q %Q  GP  GQ  FQ  GQ  "
5477       "GP *P NQ ,V  MQ  GR  HR  HR          #Q    =Q  FQ  HR  HQ  FQ   *V:Q  LQ  GQ  GQ  GQ  GY 3Y=Q !Y               "
5478       "  9X                                                                MT        +X #X      :Y      EX            "
5479       "              5X          BZ                      7Y               7]                     8X  <X #Y            "
5480       "          HY      MX            0Y  'X                 MY    CY  X  Y          ;Y         8Y 4Y   *Y    1Y    E"
5481       "X 3Y                                          CZ  IU        3X -\\I_                              KY  8WEW   $V"
5482       "              %Z                  NU                                    0R                                 #V  "
5483       "                                  )T           <Z 3Y  =Y                 8X                                    "
5484       "                            MT        +X $X      9X      DX                          6Y          AZ NR         "
5485       "            =Z               6\\                     8X  <X #Y                      HY      MX            0Y  '"
5486       "X                 NZ    CY  X  X          :Y         8Y 4Y   *Y    1Y    EX 3Y                                 "
5487       "         CZ  IU        3X -q                              JY  8WEW   #V              &Z                  NV    "
5488       "                                                                  0V                                    (R     "
5489       "      <Y 2Y  =Y                 8X                                                                MT        *X "
5490       "%X      9X      EY                          6X          @[!T                     >Z               5\\          "
5491       "           9X  ;X $Y                      HY      NY            0Y  'X                 NY    BY  X !Y          "
5492       ":Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3X -p              "
5493       "                IY  8WEW   #V              &Z                  MV                                              "
5494       "                        0U                                    'P           ;Y 2Y  >Z                 8X        "
5495       "                                                        MT        *X &X      9X      DX                        "
5496       "  5X          ?\\%W                     ?Z               4\\                     :X  ;X $Y                     "
5497       " IZ      NY            0Y  'X                 NY    BZ !X !Y          :Y         8Y 4Y   *Y    1Y    EX 3Y     "
5498       "                                     CZ  IU        3X -o                              HY  8WEW   \"V           "
5499       "   'Z                  LU                                                                      0V              "
5500       "                                  CZ 2Y  >Y                 7X                                                 "
5501       "               MT        )X 'X      9X      DX                          5W          <\\(X                     ?"
5502       "Z               3\\                     ;Y  <X $Y                      IY      MY            0Y  'X            "
5503       "     Z    AY !X !Y          :Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ  I"
5504       "U        3X -n                              GY  8WEW   \"V              '[3Q                 <V                "
5505       "                                                      0V                                                DY 1Y  "
5506       "?Z                 7X                                                                MT        )X (X      8W   "
5507       "   CX                          6X          ;],[                     AZ               1\\                     <e"
5508       "  GX 2f                      JZ      MY            0Y  'X                 Y    @Z \"X \"Z          :Y         8"
5509       "Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3X ,k                          "
5510       "    EY  8WEW   !V              'Z4R                 <V                                                         "
5511       "             0V                                                EZ 1Y  ?Y                 ARGX                  "
5512       "                                              MT        (X )X      8W      DX                          5W      "
5513       "    9^1^                     AZ               0\\                     =e  GX 2f                      KZ      LY"
5514       "            0Y  'X                !Z    @[ #X #Z          9Y         8Y 4Y   *Y    1Y    EX 3Y                 "
5515       "                         CZ  IU        3X )f                              CY  8WEW   !V              '[7T      "
5516       "           ;V                                                                      1V                          "
5517       "                      EY 0Y  ?Y                 FWGW                                                           "
5518       "     LT        'W *X      8W      CX                          5W          8`7`                     A[          "
5519       "     /\\                     >e  GX 2f                      KZ      LY            0Y  'X                !Y    >"
5520       "\\ %X &]          9Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3"
5521       "X $^                              @Y  8WEW   !V              '\\:V                 ;V                          "
5522       "                                            1W                                                GZ 0Y  @Z        "
5523       "         FWHX                                                                LT        'X +W      7W           "
5524       "                      V          5b?c                     A[               -\\                     ?e   !f     "
5525       "                <P2\\      MY            /Y  'X                \"Z    >f /X 0g          9Y         8Y 4Y   *Y  "
5526       "  1Y    EX 3Y                                          CZ  IU        3X                                5Y      "
5527       " NV              &\\=X                 ;V                                                                      "
5528       "1W                                                GY /Y  AZ                 EWHX                               "
5529       "                                 LT        &W ,X      7V                                 V          3~T        "
5530       "             A]               ,\\                     @e   !f                     <R5\\      LY            /Y  "
5531       "'X                #Z    =f /X 0f          8Y         8Y 4Y   *Y    1Y    EX 3Y                                 "
5532       "         CZ  IU        3X                                5Y       NW              '^B[                 <W      "
5533       "                                                                1W                                             "
5534       "   HZ /Y  AZ                 DWIX                                                                LT        &X -"
5535       "W      6U                                 NV          1~P                     B_               *\\             "
5536       "        Ae   !f                     <U:]      LZ            /Y  'X                #Z    <e /X 0e          7Y   "
5537       "      8Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3X                      "
5538       "          5Y       X              &aJ_                 <W                                                      "
5539       "                2X                                                IZ .Y  BZ                 CWJY               "
5540       "                                                 LT        %X /X                                               "
5541       "   7|                     Hf               )\\                     Be   !f                     <X?_      N[    "
5542       "        .Y  'X                %[    :d /X 0e          7Y         8Y 4Y   *Y    1Y    EX 3Y                     "
5543       "                     CZ  IU        3X                                5Y      -PDX              %v              "
5544       "   JQDX                                                                      ?QEY                              "
5545       "                  J[ .Y  D\\                 CXLY                                                              "
5546       "  KT                                                             7x                     Fe                     "
5547       "                                          -_Me     %b            .Y  'X                /e    9c /X 0c          "
5548       "5Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ  IU        3X                 "
5549       "               5Y      -d              $u                 Je                                                   "
5550       "                   ?d                                               $d -Y  Ne                 Ad               "
5551       "                                                 KT                                                            "
5552       " 5s                     Cd                                                               ,v     %b            -"
5553       "Y  'X                0e    6a /X 0b          4Y         8Y 4Y   *Y    1Y    EX 3Y                              "
5554       "            CZ  IU        3X                                5Y      -d              #t                 Jd      "
5555       "                                                                >d                                             "
5556       "  %e -Y  Nd                 @c                                                                                 "
5557       "                                            (m                     @c                                          "
5558       "                     +u     $b            -Y  'X                0d    2^ /X 0_          1Y         8Y 4Y   *Y  "
5559       "  1Y    EX 3Y                                          CZ  IT        2X                                5Y      "
5560       "-c              !q                 Hd                                                                      >c  "
5561       "                                             $d ,Y  Nd                 ?b                                      "
5562       "                                                                                       %g                     ="
5563       "b                                                               *t     #a            ,Y  'X                0d  "
5564       "  ,X /X 0Y          +Y         8Y 4Y   *Y    1Y    EX 3Y                                          CZ          '"
5565       "X                                5Y      -c               Nm                 Fc                                "
5566       "                                      =c                                               $c +Y  Nc               "
5567       "  >a                                                                                                           "
5568       "                   M\\                     8a                                             \"~Y                1"
5569       "r     !`            +Y  'X                0c      1X            1Y         8Y 4Y   *Y    1Y    EX 3Y           "
5570       "                               CZ          &W                                5Y      -b               Lj       "
5571       "          Db                                                                      <b                           "
5572       "                    #b *Y  Nb                 <_                                                               "
5573       "                                                                                    (_                         "
5574       "                     ~Y                1q      _            *Y  'X                0b      0X            1Y     "
5575       "    8Y 4Y   *Y    1Y    EX 3Y                                          CZ                                      "
5576       "     3Y      -`               He                 A`                                                            "
5577       "          :`                                               !a )Y  Na                 :]                        "
5578       "                                                                                                               "
5579       "            ']                                              M~Y                .l      M]            (Y  'X    "
5580       "            0`      .X            1Y         8Y 4Y   *Y    1Y    EX 3Y                                         "
5581       "                                             KY      *Z               B^                 9Z                    "
5582       "                                                  5Z                                                M` (Y  N`  "
5583       "               8Z                                                                                              "
5584       "                                                     %X                                              H~Y       "
5585       "         *d      I[            &Y  'X                0^      ,X            1Y         8Y 4Y   *Y    1Y    EX 3Y"
5586       "                                                                                      KY                       "
5587       "                                                                                                               "
5588       "                         H^ &Y  N]                 3V                                                          "
5589       "                                                                                                               "
5590       "                         B~Y                #X      CU            !X  &X                /Y      (X            1"
5591       "Y         7X 4X   )X    0Y    EX 2X                                                                            "
5592       "          KY                                                                                                   "
5593       "                                                            HZ \"X  MY                                         "
5594       "                                                                                                               "
5595       "                                                            J~Y                                                "
5596       "               9X                                                                                              "
5597       "                                                                                                               "
5598       "                                                                                                               "
5599       "                                                                                                               "
5600       "                                                                          3~Y                                  "
5601       "                             9X                                                                                "
5602       "                                                                                                               "
5603       "                                                                                                               "
5604       "                                                                                                               "
5605       "                                                                                        3~Y                    "
5606       "                                           9X                                                                  "
5607       "                                                                                                               "
5608       "                                                                                                               "
5609       "  '" };
5610 
5611     // Define a 40x38 'danger' color logo (used by cimg::dialog()).
5612     static const unsigned char logo40x38[4576] = {
5613       177,200,200,200,3,123,123,0,36,200,200,200,1,123,123,0,2,255,255,0,1,189,189,189,1,0,0,0,34,200,200,200,
5614       1,123,123,0,4,255,255,0,1,189,189,189,1,0,0,0,1,123,123,123,32,200,200,200,1,123,123,0,5,255,255,0,1,0,0,
5615       0,2,123,123,123,30,200,200,200,1,123,123,0,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,29,200,200,200,
5616       1,123,123,0,7,255,255,0,1,0,0,0,2,123,123,123,28,200,200,200,1,123,123,0,8,255,255,0,1,189,189,189,1,0,0,0,
5617       2,123,123,123,27,200,200,200,1,123,123,0,9,255,255,0,1,0,0,0,2,123,123,123,26,200,200,200,1,123,123,0,10,255,
5618       255,0,1,189,189,189,1,0,0,0,2,123,123,123,25,200,200,200,1,123,123,0,3,255,255,0,1,189,189,189,3,0,0,0,1,189,
5619       189,189,3,255,255,0,1,0,0,0,2,123,123,123,24,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,3,255,255,0,1,189,
5620       189,189,1,0,0,0,2,123,123,123,23,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,4,255,255,0,1,0,0,0,2,123,123,123,
5621       22,200,200,200,1,123,123,0,5,255,255,0,5,0,0,0,4,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,21,200,200,200,
5622       1,123,123,0,5,255,255,0,5,0,0,0,5,255,255,0,1,0,0,0,2,123,123,123,20,200,200,200,1,123,123,0,6,255,255,0,5,0,0,
5623       0,5,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,19,200,200,200,1,123,123,0,6,255,255,0,1,123,123,0,3,0,0,0,1,
5624       123,123,0,6,255,255,0,1,0,0,0,2,123,123,123,18,200,200,200,1,123,123,0,7,255,255,0,1,189,189,189,3,0,0,0,1,189,
5625       189,189,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,17,200,200,200,1,123,123,0,8,255,255,0,3,0,0,0,8,255,255,
5626       0,1,0,0,0,2,123,123,123,16,200,200,200,1,123,123,0,9,255,255,0,1,123,123,0,1,0,0,0,1,123,123,0,8,255,255,0,1,189,
5627       189,189,1,0,0,0,2,123,123,123,15,200,200,200,1,123,123,0,9,255,255,0,1,189,189,189,1,0,0,0,1,189,189,189,9,255,
5628       255,0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2,
5629       123,123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0,
5630       1,189,189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11,
5631       255,255,0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0,
5632       1,189,189,189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11,
5633       255,255,0,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123,
5634       123,0,26,255,255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0,
5635       0,4,123,123,123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25,
5636       123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 };
5637 
5638     //! Get/set default output stream for the \CImg library messages.
5639     /**
5640        \param file Desired output stream. Set to \c 0 to get the currently used output stream only.
5641        \return Currently used output stream.
5642     **/
5643     inline std::FILE* output(std::FILE *file) {
5644       cimg::mutex(1);
5645       static std::FILE *res = cimg::_stderr();
5646       if (file) res = file;
5647       cimg::mutex(1,0);
5648       return res;
5649     }
5650 
5651     // Return number of available CPU cores.
5652     inline unsigned int nb_cpus() {
5653       unsigned int res = 1;
5654 #if cimg_OS==2
5655       SYSTEM_INFO sysinfo;
5656       GetSystemInfo(&sysinfo);
5657       res = (unsigned int)sysinfo.dwNumberOfProcessors;
5658 #elif cimg_OS == 1
5659       res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
5660 #endif
5661       return res?res:1U;
5662     }
5663 
5664     // Lock/unlock mutex for CImg multi-thread programming.
5665     inline int mutex(const unsigned int n, const int lock_mode) {
5666       switch (lock_mode) {
5667       case 0 : cimg::Mutex_attr().unlock(n); return 0;
5668       case 1 : cimg::Mutex_attr().lock(n); return 0;
5669       default : return cimg::Mutex_attr().trylock(n);
5670       }
5671     }
5672 
5673     //! Display a warning message on the default output stream.
5674     /**
5675        \param format C-string containing the format of the message, as with <tt>std::printf()</tt>.
5676        \note If configuration macro \c cimg_strict_warnings is set, this function throws a
5677        \c CImgWarningException instead.
5678        \warning As the first argument is a format string, it is highly recommended to write
5679        \code
5680        cimg::warn("%s",warning_message);
5681        \endcode
5682        instead of
5683        \code
5684        cimg::warn(warning_message);
5685        \endcode
5686        if \c warning_message can be arbitrary, to prevent nasty memory access.
5687     **/
5688     inline void warn(const char *const format, ...) {
5689       if (cimg::exception_mode()>=1) {
5690         char *const message = new char[16384];
5691         std::va_list ap;
5692         va_start(ap,format);
5693         cimg_vsnprintf(message,16384,format,ap);
5694         va_end(ap);
5695 #ifdef cimg_strict_warnings
5696         throw CImgWarningException(message);
5697 #else
5698         std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message);
5699 #endif
5700         delete[] message;
5701       }
5702     }
5703 
5704     // Execute an external system command.
5705     /**
5706        \param command C-string containing the command line to execute.
5707        \param module_name Module name.
5708        \return Status value of the executed command, whose meaning is OS-dependent.
5709        \note This function is similar to <tt>std::system()</tt>
5710        but it does not open an extra console windows
5711        on Windows-based systems.
5712     **/
5713     inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) {
5714       cimg::unused(module_name);
5715 #ifdef cimg_no_system_calls
5716       return -1;
5717 #else
5718       if (is_verbose) return std::system(command);
5719 #if cimg_OS==1
5720       const unsigned int l = (unsigned int)std::strlen(command);
5721       if (l) {
5722         char *const ncommand = new char[l + 24];
5723         std::memcpy(ncommand,command,l);
5724         std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent
5725         const int out_val = std::system(ncommand);
5726         delete[] ncommand;
5727         return out_val;
5728       } else return -1;
5729 #elif cimg_OS==2
5730       PROCESS_INFORMATION pi;
5731       STARTUPINFOA si;
5732       std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
5733       std::memset(&si,0,sizeof(STARTUPINFO));
5734       GetStartupInfoA(&si);
5735       si.cb = sizeof(si);
5736       si.wShowWindow = SW_HIDE;
5737       si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
5738       const BOOL res = CreateProcessA((LPCSTR)module_name,(LPSTR)command,0,0,FALSE,0,0,0,&si,&pi);
5739       if (res) {
5740         WaitForSingleObject(pi.hProcess,INFINITE);
5741         CloseHandle(pi.hThread);
5742         CloseHandle(pi.hProcess);
5743         return 0;
5744       } else {
5745         char* lpMsgBuf;
5746 
5747         // Get the error message.
5748         DWORD errorCode = GetLastError();
5749         FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
5750                        0,errorCode,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPSTR)&lpMsgBuf,0,0);
5751         cimg::warn("cimg::system() : Command '%s' (module name '%s) failed with error %lu: %s",
5752                    module_name==0?"(null)":module_name,
5753                    command==0?"(null)":command,
5754                    errorCode,lpMsgBuf);
5755         return -1;
5756       }
5757 #else
5758       return std::system(command);
5759 #endif
5760 #endif
5761     }
5762 
5763     //! Return a reference to a temporary variable of type T.
5764     template<typename T>
5765     inline T& temporary(const T&) {
5766       static T temp;
5767       return temp;
5768     }
5769 
5770     //! Exchange values of variables \c a and \c b.
5771     template<typename T>
5772     inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
5773 
5774     //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2).
5775     template<typename T1, typename T2>
5776     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
5777       cimg::swap(a1,b1); cimg::swap(a2,b2);
5778     }
5779 
5780     //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3).
5781     template<typename T1, typename T2, typename T3>
5782     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
5783       cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
5784     }
5785 
5786     //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4).
5787     template<typename T1, typename T2, typename T3, typename T4>
5788     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
5789       cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
5790     }
5791 
5792     //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5).
5793     template<typename T1, typename T2, typename T3, typename T4, typename T5>
5794     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
5795       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
5796     }
5797 
5798     //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6).
5799     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
5800     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) {
5801       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
5802     }
5803 
5804     //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7).
5805     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
5806     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6,
5807                      T7& a7, T7& b7) {
5808       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
5809     }
5810 
5811     //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8).
5812     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
5813     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6,
5814                      T7& a7, T7& b7, T8& a8, T8& b8) {
5815       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
5816     }
5817 
5818     //! Return the endianness of the current architecture.
5819     /**
5820        \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>.
5821     **/
5822     inline bool endianness() {
5823       const int x = 1;
5824       return ((unsigned char*)&x)[0]?false:true;
5825     }
5826 
5827     //! Reverse endianness of all elements in a memory buffer.
5828     /**
5829        \param[in,out] buffer Memory buffer whose endianness must be reversed.
5830        \param size Number of buffer elements to reverse.
5831     **/
5832     template<typename T>
5833     inline void invert_endianness(T* const buffer, const cimg_ulong size) {
5834       if (size) switch (sizeof(T)) {
5835         case 1 : break;
5836         case 2 : {
5837           for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) {
5838             const unsigned short val = *(--ptr);
5839             *ptr = (unsigned short)((val>>8) | ((val<<8)));
5840           }
5841         } break;
5842         case 4 : {
5843           for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) {
5844             const unsigned int val = *(--ptr);
5845             *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24);
5846           }
5847         } break;
5848         case 8 : {
5849           const cimg_uint64
5850             m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24,
5851             m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56;
5852           for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) {
5853             const cimg_uint64 val = *(--ptr);
5854             *ptr =  (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) |
5855                      ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56));
5856           }
5857         } break;
5858         default : {
5859           for (T* ptr = buffer + size; ptr>buffer; ) {
5860             unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
5861             for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
5862           }
5863         }
5864         }
5865     }
5866     inline void invert_endianness(bool* const, const cimg_ulong) {}
5867     inline void invert_endianness(unsigned char* const, const cimg_ulong) {}
5868     inline void invert_endianness(char* const, const cimg_ulong) {}
5869 
5870     //! Reverse endianness of a single variable.
5871     /**
5872        \param[in,out] a Variable to reverse.
5873        \return Reference to reversed variable.
5874     **/
5875     template<typename T>
5876     inline T& invert_endianness(T& a) {
5877       invert_endianness(&a,1);
5878       return a;
5879     }
5880 
5881     // Conversion functions to get more precision when trying to store unsigned ints values as floats.
5882     inline unsigned int float2uint(const float f) {
5883       int tmp = 0;
5884       std::memcpy(&tmp,&f,sizeof(float));
5885       if (tmp>=0) return (unsigned int)f;
5886       unsigned int u;
5887       // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
5888       std::memcpy(&u,&f,sizeof(float));
5889       return ((u)<<2)>>2; // set sign & exponent bit to 0
5890     }
5891 
5892     inline float uint2float(const unsigned int u) {
5893       if (u<(1U<<19)) return (float)u;  // Consider safe storage of unsigned int as floats until 19bits (i.e 524287)
5894       float f;
5895       const unsigned int v = u|(3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1
5896       // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
5897       std::memcpy(&f,&v,sizeof(float));
5898       return f;
5899     }
5900 
5901     //! Return the value of a system timer, with a millisecond precision.
5902     /**
5903        \note The timer does not necessarily starts from \c 0.
5904     **/
5905     inline cimg_uint64 time() {
5906 #if cimg_OS==1
5907       struct timeval st_time;
5908       gettimeofday(&st_time,0);
5909       return (cimg_uint64)st_time.tv_sec*1000 + (cimg_uint64)st_time.tv_usec/1000;
5910 #elif cimg_OS==2
5911       ULARGE_INTEGER ul;
5912       FILETIME ft;
5913       GetSystemTimeAsFileTime(&ft);
5914       ul.LowPart = ft.dwLowDateTime;
5915       ul.HighPart = ft.dwHighDateTime;
5916       return (cimg_uint64)ul.QuadPart/10000;
5917 #else
5918       return 0;
5919 #endif
5920     }
5921 
5922     // Implement a tic/toc mechanism to display elapsed time of algorithms.
5923     inline cimg_uint64 tictoc(const bool is_tic);
5924 
5925     //! Start tic/toc timer for time measurement between code instructions.
5926     /**
5927        \return Current value of the timer (same value as time()).
5928     **/
5929     inline cimg_uint64 tic() {
5930       return cimg::tictoc(true);
5931     }
5932 
5933     //! End tic/toc timer and displays elapsed time from last call to tic().
5934     /**
5935        \return Time elapsed (in ms) since last call to tic().
5936     **/
5937     inline cimg_uint64 toc() {
5938       return cimg::tictoc(false);
5939     }
5940 
5941     //! Sleep for a given numbers of milliseconds.
5942     /**
5943        \param milliseconds Number of milliseconds to wait for.
5944        \note This function frees the CPU resources during the sleeping time.
5945        It can be used to temporize your program properly, without wasting CPU time.
5946     **/
5947     inline void sleep(const unsigned int milliseconds) {
5948 #if cimg_OS==1
5949       struct timespec tv;
5950       tv.tv_sec = milliseconds/1000;
5951       tv.tv_nsec = (milliseconds%1000)*1000000;
5952       nanosleep(&tv,0);
5953 #elif cimg_OS==2
5954       Sleep(milliseconds);
5955 #else
5956       cimg::unused(milliseconds);
5957 #endif
5958     }
5959 
5960     inline unsigned int wait(const unsigned int milliseconds, cimg_uint64 *const p_timer) {
5961       if (!*p_timer) *p_timer = cimg::time();
5962       const cimg_uint64 current_time = cimg::time();
5963       if (current_time<*p_timer || current_time>=*p_timer + milliseconds) { *p_timer = current_time; return 0; }
5964       const unsigned int time_diff = (unsigned int)(*p_timer + milliseconds - current_time);
5965       *p_timer = current_time + time_diff;
5966       cimg::sleep(time_diff);
5967       return time_diff;
5968     }
5969 
5970     //! Wait for a given number of milliseconds since the last call to wait().
5971     /**
5972        \param milliseconds Number of milliseconds to wait for.
5973        \return Number of milliseconds elapsed since the last call to wait().
5974        \note Same as sleep() with a waiting time computed with regard to the last call
5975        of wait(). It may be used to temporize your program properly, without wasting CPU time.
5976     **/
5977     inline unsigned int wait(const unsigned int milliseconds) {
5978       cimg::mutex(3);
5979       static cimg_uint64 timer = cimg::time();
5980       cimg::mutex(3,0);
5981       return cimg::wait(milliseconds,&timer);
5982     }
5983 
5984     // Custom random number generator (allow re-entrance).
5985     inline cimg_uint64& rng() { // Used as a shared global number for rng
5986       static cimg_uint64 rng = 0xB16B00B5U;
5987       return rng;
5988     }
5989 
5990     inline unsigned int _rand(cimg_uint64 *const p_rng) {
5991       *p_rng = *p_rng*1103515245 + 12345U;
5992       return (unsigned int)*p_rng;
5993     }
5994 
5995     inline unsigned int _rand() {
5996       cimg::mutex(4);
5997       const unsigned int res = cimg::_rand(&cimg::rng());
5998       cimg::mutex(4,0);
5999       return res;
6000     }
6001 
6002     inline void srand(cimg_uint64 *const p_rng) {
6003 #if cimg_OS==1
6004       *p_rng = cimg::time() + (cimg_uint64)getpid();
6005 #elif cimg_OS==2
6006       *p_rng = cimg::time() + (cimg_uint64)_getpid();
6007 #endif
6008     }
6009 
6010     inline void srand() {
6011       cimg::mutex(4);
6012       cimg::srand(&cimg::rng());
6013       cimg::mutex(4,0);
6014     }
6015 
6016     inline void srand(const cimg_uint64 seed) {
6017       cimg::mutex(4);
6018       cimg::rng() = seed;
6019       cimg::mutex(4,0);
6020     }
6021 
6022     inline double rand(const double val_min, const double val_max, cimg_uint64 *const p_rng) {
6023       const double val = cimg::_rand(p_rng)/(double)~0U;
6024       return val_min + (val_max - val_min)*val;
6025     }
6026 
6027     inline double rand(const double val_min, const double val_max) {
6028       cimg::mutex(4);
6029       const double res = cimg::rand(val_min,val_max,&cimg::rng());
6030       cimg::mutex(4,0);
6031       return res;
6032     }
6033 
6034     inline double rand(const double val_max, cimg_uint64 *const p_rng) {
6035       const double val = cimg::_rand(p_rng)/(double)~0U;
6036       return val_max*val;
6037     }
6038 
6039     inline double rand(const double val_max=1) {
6040       cimg::mutex(4);
6041       const double res = cimg::rand(val_max,&cimg::rng());
6042       cimg::mutex(4,0);
6043       return res;
6044     }
6045 
6046     inline double grand(cimg_uint64 *const p_rng) {
6047       double x1, w;
6048       do {
6049         const double x2 = cimg::rand(-1,1,p_rng);
6050         x1 = cimg::rand(-1,1,p_rng);
6051         w = x1*x1 + x2*x2;
6052       } while (w<=0 || w>=1.);
6053       return x1*std::sqrt((-2*std::log(w))/w);
6054     }
6055 
6056     inline double grand() {
6057       cimg::mutex(4);
6058       const double res = cimg::grand(&cimg::rng());
6059       cimg::mutex(4,0);
6060       return res;
6061     }
6062 
6063     inline unsigned int prand(const double z, cimg_uint64 *const p_rng) {
6064       if (z<=1.e-10) return 0;
6065       if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand(p_rng)) + z);
6066       unsigned int k = 0;
6067       const double y = std::exp(-z);
6068       for (double s = 1.; s>=y; ++k) s*=cimg::rand(1,p_rng);
6069       return k - 1;
6070     }
6071 
6072     inline unsigned int prand(const double z) {
6073       cimg::mutex(4);
6074       const unsigned int res = cimg::prand(z,&cimg::rng());
6075       cimg::mutex(4,0);
6076       return res;
6077     }
6078 
6079     //! Cut (i.e. clamp) value in specified interval.
6080     template<typename T, typename t>
6081     inline T cut(const T& val, const t& val_min, const t& val_max) {
6082       return val<=val_min?(T)val_min:val>=val_max?(T)val_max:val;
6083     }
6084 
6085     //! Bitwise-rotate value on the left.
6086     template<typename T>
6087     inline T rol(const T& a, const unsigned int n=1) {
6088       return n?(T)((a<<n)|(a>>((sizeof(T)<<3) - n))):a;
6089     }
6090 
6091     inline float rol(const float a, const unsigned int n=1) {
6092       return (float)rol((int)a,n);
6093     }
6094 
6095     inline double rol(const double a, const unsigned int n=1) {
6096       return (double)rol((cimg_long)a,n);
6097     }
6098 
6099     inline double rol(const long double a, const unsigned int n=1) {
6100       return (double)rol((cimg_long)a,n);
6101     }
6102 
6103 #ifdef cimg_use_half
6104     inline half rol(const half a, const unsigned int n=1) {
6105       return (half)rol((int)a,n);
6106     }
6107 #endif
6108 
6109     //! Bitwise-rotate value on the right.
6110     template<typename T>
6111     inline T ror(const T& a, const unsigned int n=1) {
6112       return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a;
6113     }
6114 
6115     inline float ror(const float a, const unsigned int n=1) {
6116       return (float)ror((int)a,n);
6117     }
6118 
6119     inline double ror(const double a, const unsigned int n=1) {
6120       return (double)ror((cimg_long)a,n);
6121     }
6122 
6123     inline double ror(const long double a, const unsigned int n=1) {
6124       return (double)ror((cimg_long)a,n);
6125     }
6126 
6127 #ifdef cimg_use_half
6128     inline half ror(const half a, const unsigned int n=1) {
6129       return (half)ror((int)a,n);
6130     }
6131 #endif
6132 
6133     //! Return absolute value of a value.
6134     template<typename T>
6135     inline T abs(const T& a) {
6136       return a>=0?a:-a;
6137     }
6138     inline bool abs(const bool a) {
6139       return a;
6140     }
6141     inline int abs(const unsigned char a) {
6142       return (int)a;
6143     }
6144     inline int abs(const unsigned short a) {
6145       return (int)a;
6146     }
6147     inline int abs(const unsigned int a) {
6148       return (int)a;
6149     }
6150     inline int abs(const int a) {
6151       return std::abs(a);
6152     }
6153     inline cimg_int64 abs(const cimg_uint64 a) {
6154       return (cimg_int64)a;
6155     }
6156     inline double abs(const double a) {
6157       return std::fabs(a);
6158     }
6159     inline float abs(const float a) {
6160       return (float)std::fabs((double)a);
6161     }
6162 
6163     //! Return hyperbolic arcosine of a value.
6164     inline double acosh(const double x) {
6165 #if cimg_use_cpp11==1 && !defined(_MSC_VER)
6166       return std::acosh(x);
6167 #else
6168       return std::log(x + std::sqrt(x*x - 1));
6169 #endif
6170     }
6171 
6172     //! Return hyperbolic arcsine of a value.
6173     inline double asinh(const double x) {
6174 #if cimg_use_cpp11==1 && !defined(_MSC_VER)
6175       return std::asinh(x);
6176 #else
6177       return std::log(x + std::sqrt(x*x + 1));
6178 #endif
6179     }
6180 
6181     //! Return hyperbolic arctangent of a value.
6182     inline double atanh(const double x) {
6183 #if cimg_use_cpp11==1 && !defined(_MSC_VER)
6184       return std::atanh(x);
6185 #else
6186       return 0.5*std::log((1. + x)/(1. - x));
6187 #endif
6188     }
6189 
6190     //! Return the sinc of a given value.
6191     inline double sinc(const double x) {
6192       return x?std::sin(x)/x:1;
6193     }
6194 
6195     //! Return base-2 logarithm of a value.
6196     inline double log2(const double x) {
6197 #if cimg_use_cpp11==1 && !defined(_MSC_VER)
6198       return std::log2(x);
6199 #else
6200       const double base2 = std::log(2.);
6201       return std::log(x)/base2;
6202 #endif
6203     }
6204 
6205     //! Return square of a value.
6206     template<typename T>
6207     inline T sqr(const T& val) {
6208       return val*val;
6209     }
6210 
6211     //! Return cubic root of a value.
6212     template<typename T>
6213     inline double cbrt(const T& x) {
6214 #if cimg_use_cpp11==1
6215       return std::cbrt(x);
6216 #else
6217       return x>=0?std::pow((double)x,1./3):-std::pow(-(double)x,1./3);
6218 #endif
6219     }
6220 
6221     template<typename T>
6222     inline T pow3(const T& val) {
6223       return val*val*val;
6224     }
6225     template<typename T>
6226     inline T pow4(const T& val) {
6227       return val*val*val*val;
6228     }
6229 
6230     //! Return the minimum between three values.
6231     template<typename t>
6232     inline t min(const t& a, const t& b, const t& c) {
6233       return std::min(std::min(a,b),c);
6234     }
6235 
6236     //! Return the minimum between four values.
6237     template<typename t>
6238     inline t min(const t& a, const t& b, const t& c, const t& d) {
6239       return std::min(std::min(a,b),std::min(c,d));
6240     }
6241 
6242     //! Return the minabs between two values.
6243     template<typename t>
6244     inline t minabs(const t& a, const t& b) {
6245       return cimg::abs(b)<cimg::abs(a)?b:a;
6246     }
6247 
6248     template<typename t>
6249     inline t minabs(const t& a, const t& b, const t& abs_b) {
6250       return abs_b<cimg::abs(a)?b:a;
6251     }
6252 
6253     //! Return the maximum between three values.
6254     template<typename t>
6255     inline t max(const t& a, const t& b, const t& c) {
6256       return std::max(std::max(a,b),c);
6257     }
6258 
6259     //! Return the maximum between four values.
6260     template<typename t>
6261     inline t max(const t& a, const t& b, const t& c, const t& d) {
6262       return std::max(std::max(a,b),std::max(c,d));
6263     }
6264 
6265     //! Return the maxabs between two values.
6266     template<typename t>
6267     inline t maxabs(const t& a, const t& b) {
6268       return cimg::abs(b)>cimg::abs(a)?b:a;
6269     }
6270 
6271     template<typename t>
6272     inline t maxabs(const t& a, const t& b, const t& abs_b) {
6273       return abs_b>cimg::abs(a)?b:a;
6274     }
6275 
6276     //! Return the sign of a value.
6277     template<typename T>
6278     inline T sign(const T& x) {
6279       return (T)(cimg::type<T>::is_nan(x)?0:x<0?-1:x>0);
6280     }
6281 
6282     //! Return the nearest power of 2 higher than given value.
6283     template<typename T>
6284     inline cimg_uint64 nearest_pow2(const T& x) {
6285       cimg_uint64 i = 1;
6286       while (x>i) i<<=1;
6287       return i;
6288     }
6289 
6290     //! Return the modulo of a value.
6291     /**
6292        \param x Input value.
6293        \param m Modulo value.
6294        \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type.
6295     **/
6296     template<typename T>
6297     inline T mod(const T& x, const T& m) {
6298       const double dx = (double)x, dm = (double)m;
6299       if (!cimg::type<double>::is_finite(dm)) return x;
6300       if (cimg::type<double>::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm));
6301       return (T)0;
6302     }
6303     inline int mod(const bool x, const bool m) {
6304       return m?(x?1:0):0;
6305     }
6306     inline int mod(const unsigned char x, const unsigned char m) {
6307       return x%m;
6308     }
6309     inline int mod(const char x, const char m) {
6310 #if defined(CHAR_MAX) && CHAR_MAX==255
6311       return x%m;
6312 #else
6313       return x>=0?x%m:(x%m?m + x%m:0);
6314 #endif
6315     }
6316     inline int mod(const unsigned short x, const unsigned short m) {
6317       return x%m;
6318     }
6319     inline int mod(const short x, const short m) {
6320       return x>=0?x%m:(x%m?m + x%m:0);
6321     }
6322     inline int mod(const unsigned int x, const unsigned int m) {
6323       return (int)(x%m);
6324     }
6325     inline int mod(const int x, const int m) {
6326       return x>=0?x%m:(x%m?m + x%m:0);
6327     }
6328     inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) {
6329       return x%m;
6330     }
6331     inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) {
6332       return x>=0?x%m:(x%m?m + x%m:0);
6333     }
6334 
6335     //! Return the min-mod of two values.
6336     /**
6337        \note <i>minmod(\p a,\p b)</i> is defined to be:
6338        - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
6339        - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
6340     **/
6341     template<typename T>
6342     inline T minmod(const T& a, const T& b) {
6343       return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
6344     }
6345 
6346     template<typename T>
6347     inline T round(const T& x) {
6348       return (T)std::floor((_cimg_Tfloat)x + 0.5f);
6349     }
6350 
6351     template<typename T>
6352     inline int uiround(const T x) {
6353       return cimg::type<T>::is_float()?(int)(x + 0.5f):(int)x;
6354     }
6355 
6356     //! Return rounded value.
6357     /**
6358        \param x Value to be rounded.
6359        \param y Rounding precision.
6360        \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward).
6361        \return Rounded value, having the same type as input value \c x.
6362     **/
6363     template<typename T>
6364     inline T round(const T& x, const double y, const int rounding_type=0) {
6365       if (y<=0) return x;
6366       if (y==1) switch (rounding_type) {
6367         case 0 : return cimg::round(x);
6368         case 1 : return (T)std::ceil((_cimg_Tfloat)x);
6369         default : return (T)std::floor((_cimg_Tfloat)x);
6370         }
6371       const double sx = (double)x/y, floor = std::floor(sx), delta =  sx - floor;
6372       return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
6373     }
6374 
6375     // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values.
6376     // (contribution by RawTherapee: http://rawtherapee.com/).
6377     template<typename T>
6378     inline T median(T val0, T val1) {
6379       return (val0 + val1)/2;
6380     }
6381 
6382     template<typename T>
6383     inline T median(T val0, T val1, T val2) {
6384       return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1)));
6385     }
6386 
6387     template<typename T>
6388     inline T median(T val0, T val1, T val2, T val3, T val4) {
6389       T tmp = std::min(val0,val1);
6390       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
6391       val3 = std::max(val0,tmp);  val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2);
6392       val1 = tmp; tmp = std::min(val2,val3);
6393       return std::max(val1,tmp);
6394     }
6395 
6396     template<typename T>
6397     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) {
6398       T tmp = std::min(val0,val5);
6399       val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp;
6400       tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4);
6401       val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5);
6402       val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6);
6403       val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp);
6404       tmp = std::min(val1,tmp); val3 = std::max(tmp,val3);
6405       return std::min(val3,val4);
6406     }
6407 
6408     template<typename T>
6409     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) {
6410       T tmp = std::min(val1,val2);
6411       val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
6412       val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
6413       val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1);
6414       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4);
6415       val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7);
6416       val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2);
6417       val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
6418       val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
6419       val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8);
6420       val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6);
6421       val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7);
6422       tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp);
6423       return std::min(val4,val2);
6424     }
6425 
6426     template<typename T>
6427     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,
6428                     T val12) {
6429       T tmp = std::min(val1,val7);
6430       val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp;
6431       tmp = std::min(val3,val4);  val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8);
6432       val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12);
6433       val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1);
6434       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp;
6435       tmp = std::min(val4,val6);  val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11);
6436       val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp;
6437       tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2);
6438       val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
6439       tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4);
6440       val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
6441       tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12);
6442       tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10);
6443       val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp;
6444       tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9);
6445       val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp;
6446       tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3);
6447       tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10);
6448       val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8);
6449       val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp);
6450       val5 = std::max(tmp,val5); val6 = std::min(val6,val7);
6451       return std::max(val5,val6);
6452     }
6453 
6454     template<typename T>
6455     inline T median(T val0, T val1, T val2, T val3, T val4,
6456                     T val5, T val6, T val7, T val8, T val9,
6457                     T val10, T val11, T val12, T val13, T val14,
6458                     T val15, T val16, T val17, T val18, T val19,
6459                     T val20, T val21, T val22, T val23, T val24) {
6460       T tmp = std::min(val0,val1);
6461       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
6462       val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3);
6463       val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp;
6464       tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6);
6465       tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10);
6466       val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9);
6467       tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13);
6468       val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12);
6469       tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16);
6470       val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15);
6471       tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19);
6472       val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18);
6473       tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22);
6474       val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21);
6475       tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5);
6476       val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp;
6477       tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3);
6478       tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7);
6479       val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14);
6480       val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14);
6481       val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15);
6482       val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15);
6483       val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16);
6484       val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16);
6485       val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23);
6486       val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23);
6487       val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24);
6488       val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24);
6489       val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22);
6490       val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18);
6491       val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18);
6492       val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp;
6493       tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10);
6494       val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp;
6495       tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11);
6496       tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21);
6497       val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12);
6498       tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22);
6499       val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23);
6500       val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23);
6501       val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24);
6502       val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15);
6503       val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19);
6504       tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp);
6505       val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11);
6506       val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17);
6507       tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12);
6508       val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14);
6509       val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7);
6510       tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14);
6511       tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12);
6512       val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp);
6513       val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp;
6514       val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp;
6515       tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp);
6516       val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp;
6517       tmp = std::min(val10,val20);
6518       return std::max(tmp,val12);
6519     }
6520 
6521     template<typename T>
6522     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6,
6523                     T val7, T val8, T val9, T val10, T val11, T val12, T val13,
6524                     T val14, T val15, T val16, T val17, T val18, T val19, T val20,
6525                     T val21, T val22, T val23, T val24, T val25, T val26, T val27,
6526                     T val28, T val29, T val30, T val31, T val32, T val33, T val34,
6527                     T val35, T val36, T val37, T val38, T val39, T val40, T val41,
6528                     T val42, T val43, T val44, T val45, T val46, T val47, T val48) {
6529       T tmp = std::min(val0,val32);
6530       val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp;
6531       tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35);
6532       val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp;
6533       tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38);
6534       val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp;
6535       tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41);
6536       val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42);
6537       val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp;
6538       tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45);
6539       val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46);
6540       val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp;
6541       tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16);
6542       val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17);
6543       val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19);
6544       val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp;
6545       tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22);
6546       val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp;
6547       tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25);
6548       val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26);
6549       val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp;
6550       tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29);
6551       val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30);
6552       val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp;
6553       tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32);
6554       val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33);
6555       val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp;
6556       tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36);
6557       val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37);
6558       val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp;
6559       tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40);
6560       val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41);
6561       val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp;
6562       tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44);
6563       val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45);
6564       val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp;
6565       tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8);
6566       val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp;
6567       tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11);
6568       val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp;
6569       tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14);
6570       val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp;
6571       tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25);
6572       val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26);
6573       val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp;
6574       tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29);
6575       val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30);
6576       val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp;
6577       tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41);
6578       val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42);
6579       val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp;
6580       tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45);
6581       val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46);
6582       val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp;
6583       tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33);
6584       val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34);
6585       val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp;
6586       tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37);
6587       val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38);
6588       val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp;
6589       tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16);
6590       val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17);
6591       val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp;
6592       tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20);
6593       val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21);
6594       val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp;
6595       tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32);
6596       val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33);
6597       val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp;
6598       tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36);
6599       val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37);
6600       val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp;
6601       tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48);
6602       val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4);
6603       val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6);
6604       val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
6605       tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13);
6606       val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14);
6607       val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp;
6608       tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21);
6609       val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22);
6610       val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp;
6611       tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29);
6612       val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30);
6613       val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp;
6614       tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37);
6615       val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38);
6616       val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp;
6617       tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45);
6618       val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46);
6619       val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp;
6620       tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33);
6621       val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34);
6622       val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp;
6623       tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41);
6624       val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42);
6625       val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp;
6626       tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16);
6627       val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17);
6628       val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp;
6629       tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24);
6630       val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25);
6631       val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp;
6632       tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32);
6633       val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33);
6634       val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp;
6635       tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40);
6636       val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41);
6637       val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp;
6638       tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48);
6639       val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8);
6640       val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10);
6641       val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp;
6642       tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17);
6643       val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18);
6644       val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp;
6645       tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25);
6646       val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26);
6647       val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp;
6648       tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33);
6649       val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34);
6650       val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp;
6651       tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41);
6652       val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42);
6653       val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp;
6654       tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2);
6655       val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp;
6656       tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7);
6657       val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp;
6658       tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14);
6659       val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15);
6660       val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp;
6661       tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22);
6662       val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23);
6663       val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp;
6664       tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30);
6665       val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31);
6666       val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp;
6667       tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38);
6668       val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39);
6669       val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp;
6670       tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46);
6671       val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47);
6672       val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33);
6673       val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp;
6674       tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40);
6675       val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41);
6676       val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp;
6677       tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48);
6678       val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16);
6679       val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp;
6680       tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21);
6681       val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24);
6682       val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp;
6683       tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29);
6684       val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32);
6685       val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp;
6686       tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37);
6687       val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40);
6688       val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp;
6689       tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45);
6690       val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48);
6691       val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9);
6692       val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
6693       tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16);
6694       val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17);
6695       val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp;
6696       tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24);
6697       val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25);
6698       val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp;
6699       tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32);
6700       val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33);
6701       val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp;
6702       tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40);
6703       val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41);
6704       val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp;
6705       tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48);
6706       val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4);
6707       val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8);
6708       val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp;
6709       tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13);
6710       val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16);
6711       val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp;
6712       tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21);
6713       val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24);
6714       val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp;
6715       tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29);
6716       val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32);
6717       val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp;
6718       tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37);
6719       val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40);
6720       val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp;
6721       tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45);
6722       val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48);
6723       val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5);
6724       val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11);
6725       val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17);
6726       val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23);
6727       val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29);
6728       val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35);
6729       val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41);
6730       val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47);
6731       val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36);
6732       val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42);
6733       val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48);
6734       val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28);
6735       val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34);
6736       val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24);
6737       val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30);
6738       val24 = std::max(val21,val24); val23 = std::min(val23,val26);
6739       return std::max(val23,val24);
6740     }
6741 
6742     //! Return sqrt(x^2 + y^2).
6743     template<typename T>
6744     inline T hypot(const T x, const T y) {
6745       return std::sqrt(x*x + y*y);
6746     }
6747 
6748     template<typename T>
6749     inline T hypot(const T x, const T y, const T z) {
6750       return std::sqrt(x*x + y*y + z*z);
6751     }
6752 
6753     template<typename T>
6754     inline T _hypot(const T x, const T y) { // Slower but more precise version
6755       T nx = cimg::abs(x), ny = cimg::abs(y), t;
6756       if (nx<ny) { t = nx; nx = ny; } else t = ny;
6757       if (nx>0) { t/=nx; return nx*std::sqrt(1 + t*t); }
6758       return 0;
6759     }
6760 
6761     //! Return the factorial of n
6762     inline double factorial(const int n) {
6763       if (n<0) return cimg::type<double>::nan();
6764       if (n<2) return 1;
6765       double res = 2;
6766       for (int i = 3; i<=n; ++i) res*=i;
6767       return res;
6768     }
6769 
6770     //! Return the number of permutations of k objects in a set of n objects.
6771     inline double permutations(const int k, const int n, const bool with_order) {
6772       if (n<0 || k<0) return cimg::type<double>::nan();
6773       if (k>n) return 0;
6774       double res = 1;
6775       for (int i = n; i>=n - k + 1; --i) res*=i;
6776       return with_order?res:res/cimg::factorial(k);
6777     }
6778 
6779     inline double _fibonacci(int exp) {
6780       double
6781         base = (1 + std::sqrt(5.))/2,
6782         result = 1/std::sqrt(5.);
6783       while (exp) {
6784         if (exp&1) result*=base;
6785         exp>>=1;
6786         base*=base;
6787       }
6788       return result;
6789     }
6790 
6791     //! Calculate fibonacci number.
6792     // (Precise up to n = 78, less precise for n>78).
6793     inline double fibonacci(const int n) {
6794       if (n<0) return cimg::type<double>::nan();
6795       if (n<3) return 1;
6796       if (n<11) {
6797         cimg_uint64 fn1 = 1, fn2 = 1, fn = 0;
6798         for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
6799         return (double)fn;
6800       }
6801       if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10
6802         return (double)((cimg_uint64)(_fibonacci(n) + 0.5));
6803 
6804       if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93
6805         cimg_uint64
6806           fn1 = ((cimg_uint64)303836)<<32 | 3861581201UL, // 1304969544928657ULL (avoid C++98 warning with ULL)
6807           fn2 = ((cimg_uint64)187781)<<32 | 2279239217UL, // 806515533049393ULL
6808           fn = 0;
6809         for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
6810         return (double)fn;
6811       }
6812       return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation
6813     }
6814 
6815     //! Calculate greatest common divisor.
6816     inline long gcd(long a, long b) {
6817       while (a) { const long c = a; a = b%a; b = c; }
6818       return b;
6819     }
6820 
6821     //! Convert character to lower case.
6822     inline char lowercase(const char x) {
6823       return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a');
6824     }
6825     inline double lowercase(const double x) {
6826       return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a');
6827     }
6828 
6829     //! Convert C-string to lower case.
6830     inline void lowercase(char *const str) {
6831       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr);
6832     }
6833 
6834     //! Convert character to upper case.
6835     inline char uppercase(const char x) {
6836       return (char)((x<'a'||x>'z')?x:x - 'a' + 'A');
6837     }
6838 
6839     inline double uppercase(const double x) {
6840       return (double)((x<'a'||x>'z')?x:x - 'a' + 'A');
6841     }
6842 
6843     //! Convert C-string to upper case.
6844     inline void uppercase(char *const str) {
6845       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr);
6846     }
6847 
6848     //! Return \c true if input character is blank (space, tab, or non-printable character).
6849     inline bool is_blank(const char c) {
6850       return c>=0 && (unsigned char)c<=' ';
6851     }
6852 
6853     //! Read value in a C-string.
6854     /**
6855        \param str C-string containing the float value to read.
6856        \return Read value.
6857        \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings,
6858        as in <em>"1/2"</em>.
6859     **/
6860     inline double atof(const char *const str) {
6861       double x = 0, y = 1;
6862       return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0;
6863     }
6864 
6865     //! Compare the first \p l characters of two C-strings, ignoring the case.
6866     /**
6867        \param str1 C-string.
6868        \param str2 C-string.
6869        \param l Number of characters to compare.
6870        \return \c 0 if the two strings are equal, something else otherwise.
6871        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
6872     **/
6873     inline int strncasecmp(const char *const str1, const char *const str2, const int l) {
6874       if (!l) return 0;
6875       if (!str1) return str2?-1:0;
6876       const char *nstr1 = str1, *nstr2 = str2;
6877       int k, diff = 0; for (k = 0; k<l && !(diff = lowercase(*nstr1) - lowercase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
6878       return k!=l?diff:0;
6879     }
6880 
6881     //! Compare two C-strings, ignoring the case.
6882     /**
6883        \param str1 C-string.
6884        \param str2 C-string.
6885        \return \c 0 if the two strings are equal, something else otherwise.
6886        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
6887     **/
6888     inline int strcasecmp(const char *const str1, const char *const str2) {
6889       if (!str1) return str2?-1:0;
6890       const int
6891         l1 = (int)std::strlen(str1),
6892         l2 = (int)std::strlen(str2);
6893       return cimg::strncasecmp(str1,str2,1 + (l1<l2?l1:l2));
6894     }
6895 
6896     //! Ellipsize a string.
6897     /**
6898        \param str C-string.
6899        \param l Max number of characters.
6900        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
6901     **/
6902     inline char *strellipsize(char *const str, const unsigned int l=64,
6903                               const bool is_ending=true) {
6904       if (!str) return str;
6905       const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
6906       if (ls<=nl) return str;
6907       if (is_ending) std::strcpy(str + nl - 5,"(...)");
6908       else {
6909         const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
6910         std::strcpy(str + ll,"(...)");
6911         std::memmove(str + ll + 5,str + ls - lr,lr);
6912       }
6913       str[nl] = 0;
6914       return str;
6915     }
6916 
6917     //! Ellipsize a string.
6918     /**
6919        \param str C-string.
6920        \param res output C-string.
6921        \param l Max number of characters.
6922        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
6923     **/
6924     inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64,
6925                               const bool is_ending=true) {
6926       const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
6927       if (ls<=nl) { std::strcpy(res,str); return res; }
6928       if (is_ending) {
6929         std::strncpy(res,str,nl - 5);
6930         std::strcpy(res + nl -5,"(...)");
6931       } else {
6932         const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
6933         std::strncpy(res,str,ll);
6934         std::strcpy(res + ll,"(...)");
6935         std::strncpy(res + ll + 5,str + ls - lr,lr);
6936       }
6937       res[nl] = 0;
6938       return res;
6939     }
6940 
6941     //! Remove delimiters on the start and/or end of a C-string.
6942     /**
6943        \param[in,out] str C-string to work with (modified at output).
6944        \param delimiter Delimiter character code to remove.
6945        \param is_symmetric Tells if the removal is done only if delimiters are symmetric
6946        (both at the beginning and the end of \c s).
6947        \param is_iterative Tells if the removal is done if several iterations are possible.
6948        \return \c true if delimiters have been removed, \c false otherwise.
6949    **/
6950     inline bool strpare(char *const str, const char delimiter,
6951                         const bool is_symmetric, const bool is_iterative) {
6952       if (!str) return false;
6953       const int l = (int)std::strlen(str);
6954       int p, q;
6955       if (is_symmetric) for (p = 0, q = l - 1; p<q && str[p]==delimiter && str[q]==delimiter; ) {
6956           --q; ++p; if (!is_iterative) break;
6957         } else {
6958         for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
6959         for (q = l - 1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
6960       }
6961       const int n = q - p + 1;
6962       if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
6963       return false;
6964     }
6965 
6966     //! Remove white spaces on the start and/or end of a C-string.
6967     inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) {
6968       if (!str) return false;
6969       const int l = (int)std::strlen(str);
6970       int p, q;
6971       if (is_symmetric) for (p = 0, q = l - 1; p<q && is_blank(str[p]) && is_blank(str[q]); ) {
6972           --q; ++p; if (!is_iterative) break;
6973         } else {
6974         for (p = 0; p<l && is_blank(str[p]); ) { ++p; if (!is_iterative) break; }
6975         for (q = l - 1; q>p && is_blank(str[q]); ) { --q; if (!is_iterative) break; }
6976       }
6977       const int n = q - p + 1;
6978       if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
6979       return false;
6980     }
6981 
6982     //! Replace reserved characters (for Windows filename) by another character.
6983     /**
6984        \param[in,out] str C-string to work with (modified at output).
6985        \param[in] c Replacement character.
6986     **/
6987     inline void strwindows_reserved(char *const str, const char c='_') {
6988       for (char *s = str; *s; ++s) {
6989         const char i = *s;
6990         if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c;
6991       }
6992     }
6993 
6994     //! Replace escape sequences in C-strings by character values.
6995     /**
6996        \param[in,out] str C-string to work with (modified at output).
6997     **/
6998     inline void strunescape(char *const str) {
6999 #define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;
7000 
7001       unsigned char val = 0;
7002       for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) {
7003             cimg_strunescape('a','\a');
7004             cimg_strunescape('b','\b');
7005             cimg_strunescape('e',0x1B);
7006             cimg_strunescape('f','\f');
7007             cimg_strunescape('n','\n');
7008             cimg_strunescape('r','\r');
7009             cimg_strunescape('t','\t');
7010             cimg_strunescape('v','\v');
7011             cimg_strunescape('\\','\\');
7012             cimg_strunescape('\'','\'');
7013             cimg_strunescape('\"','\"');
7014             cimg_strunescape('\?','\?');
7015           case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
7016             val = *(ns++) - '0';
7017             if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
7018             if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
7019             *nd = (char)val;
7020             break;
7021           case 'x' : {
7022             char c = lowercase(*(++ns));
7023             if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
7024               val = (c<='9'?c - '0':c - 'a' + 10);
7025               c = lowercase(*(++ns));
7026               if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
7027                 (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10);
7028                 ++ns;
7029               }
7030               *nd = val;
7031             } else *nd = c;
7032           } break;
7033           case 'u' : { // UTF-8 BMP
7034             char c1, c2, c3, c4;
7035             if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
7036                 (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
7037                 (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
7038                 (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) {
7039               c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
7040               c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
7041               c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
7042               c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
7043               const unsigned int ival =
7044                 ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4;
7045               if (ival<=0x007f) *nd = (char)ival;
7046               else if (ival<=0x07ff) {
7047                 *(nd++) = (char)((ival>>6)|0xc0);
7048                 *nd = (char)((ival&0x3f)|0x80);
7049               } else {
7050                 *(nd++) = (char)((ival>>12)|0xe0);
7051                 *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
7052                 *nd = (char)((ival&0x3f)|0x80);
7053               }
7054               ns+=5;
7055             } else *nd = *(ns++);
7056           } break;
7057           case 'U' : { // UTF-8 astral planes
7058             char c1, c2, c3, c4, c5, c6, c7, c8;
7059             if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
7060                 (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
7061                 (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
7062                 (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) &&
7063                 (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) &&
7064                 (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) &&
7065                 (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) &&
7066                 (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) {
7067               c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
7068               c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
7069               c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
7070               c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
7071               c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10);
7072               c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10);
7073               c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10);
7074               c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10);
7075               const unsigned int ival =
7076                 ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) |
7077                 ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8;
7078               if (ival<=0x007f) *nd = (char)ival;
7079               else if (ival<=0x07ff) {
7080                 *(nd++) = (char)((ival>>6)|0xc0);
7081                 *nd = (char)((ival&0x3f)|0x80);
7082               } else if (ival<=0xffff) {
7083                 *(nd++) = (char)((ival>>12)|0xe0);
7084                 *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
7085                 *nd = (char)((ival&0x3f)|0x80);
7086               } else {
7087                 *(nd++) = (char)((ival>>18)|0xf0);
7088                 *(nd++) = (char)(((ival>>12)&0x3f)|0x80);
7089                 *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
7090                 *nd = (char)((ival&0x3f)|0x80);
7091               }
7092               ns+=9;
7093             } else *nd = *(ns++);
7094           } break;
7095           default : if (*ns) *nd = *(ns++);
7096           }
7097         else *nd = *(ns++);
7098     }
7099 
7100     // Return a temporary string describing the size of a memory buffer.
7101     inline const char *strbuffersize(const cimg_ulong size);
7102 
7103     // Return string that identifies the running OS.
7104     inline const char *stros() {
7105 #if defined(linux) || defined(__linux) || defined(__linux__)
7106       static const char *const str = "Linux";
7107 #elif defined(sun) || defined(__sun)
7108       static const char *const str = "Sun OS";
7109 #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__)
7110       static const char *const str = "BSD";
7111 #elif defined(sgi) || defined(__sgi)
7112       static const char *const str = "Irix";
7113 #elif defined(__MACOSX__) || defined(__APPLE__)
7114       static const char *const str = "Mac OS";
7115 #elif defined(unix) || defined(__unix) || defined(__unix__)
7116       static const char *const str = "Generic Unix";
7117 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) || \
7118   defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
7119       static const char *const str = "Windows";
7120 #else
7121       const char
7122         *const _str1 = std::getenv("OSTYPE"),
7123         *const _str2 = _str1?_str1:std::getenv("OS"),
7124         *const str = _str2?_str2:"Unknown OS";
7125 #endif
7126       return str;
7127     }
7128 
7129     //! Return the basename of a filename.
7130     inline const char* basename(const char *const s, const char separator=cimg_file_separator)  {
7131       const char *p = 0, *np = s;
7132       while (np>=s && (p=np)) np = std::strchr(np,separator) + 1;
7133       return p;
7134     }
7135 
7136     // Return a random filename.
7137     inline const char* filenamerand() {
7138       cimg::mutex(6);
7139       static char randomid[9];
7140       for (unsigned int k = 0; k<8; ++k) {
7141         const int v = (int)cimg::rand(65535)%3;
7142         randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)):
7143                              (v==1?('a' + ((int)cimg::rand(65535)%26)):
7144                               ('A' + ((int)cimg::rand(65535)%26))));
7145       }
7146       cimg::mutex(6,0);
7147       return randomid;
7148     }
7149 
7150     // Convert filename as a Windows-style filename (short path name).
7151     inline void winformat_string(char *const str) {
7152       if (str && *str) {
7153 #if cimg_OS==2
7154         char *const nstr = new char[MAX_PATH];
7155         if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
7156         delete[] nstr;
7157 #endif
7158       }
7159     }
7160 
7161     // Open a file (similar to std:: fopen(), but with wide character support on Windows).
7162     inline std::FILE *std_fopen(const char *const path, const char *const mode);
7163 
7164 
7165     //! Open a file.
7166     /**
7167        \param path Path of the filename to open.
7168        \param mode C-string describing the opening mode.
7169        \return Opened file.
7170        \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when
7171        the specified file cannot be opened, instead of returning \c 0.
7172     **/
7173     inline std::FILE *fopen(const char *const path, const char *const mode) {
7174       if (!path)
7175         throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
7176       if (!mode)
7177         throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
7178                                     path);
7179       std::FILE *res = 0;
7180       if (*path=='-' && (!path[1] || path[1]=='.')) {
7181         res = (*mode=='r')?cimg::_stdin():cimg::_stdout();
7182 #if cimg_OS==2
7183         if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode
7184 #ifdef __BORLANDC__
7185           if (setmode(_fileno(res),0x8000)==-1) res = 0;
7186 #else
7187           if (_setmode(_fileno(res),0x8000)==-1) res = 0;
7188 #endif
7189         }
7190 #endif
7191       } else res = cimg::std_fopen(path,mode);
7192       if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
7193                                       path,mode);
7194       return res;
7195     }
7196 
7197     //! Close a file.
7198     /**
7199        \param file File to close.
7200        \return \c 0 if file has been closed properly, something else otherwise.
7201        \note Same as <tt>std::fclose()</tt> but display a warning message if
7202        the file has not been closed properly.
7203     **/
7204     inline int fclose(std::FILE *file) {
7205       if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; }
7206       if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0;
7207       const int errn = std::fclose(file);
7208       if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
7209                         errn);
7210       return errn;
7211     }
7212 
7213     //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows).
7214     inline int fseek(FILE *stream, cimg_long offset, int origin) {
7215 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
7216       return _fseeki64(stream,(__int64)offset,origin);
7217 #else
7218       return std::fseek(stream,offset,origin);
7219 #endif
7220     }
7221 
7222     //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows).
7223     inline cimg_long ftell(FILE *stream) {
7224 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
7225       return (cimg_long)_ftelli64(stream);
7226 #else
7227       return (cimg_long)std::ftell(stream);
7228 #endif
7229     }
7230 
7231     // Get the file or directory attributes with support for UTF-8 paths (Windows only).
7232 #if cimg_OS==2
7233     inline DWORD win_getfileattributes(const char *const path);
7234 #endif
7235 
7236     //! Check if a path is a directory.
7237     /**
7238        \param path Specified path to test.
7239     **/
7240     inline bool is_directory(const char *const path) {
7241       if (!path || !*path) return false;
7242 #if cimg_OS==1
7243       struct stat st_buf;
7244       return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode));
7245 #elif cimg_OS==2
7246       const DWORD res = win_getfileattributes(path);
7247       return res!=INVALID_FILE_ATTRIBUTES && (res&FILE_ATTRIBUTE_DIRECTORY);
7248 #else
7249       return false;
7250 #endif
7251     }
7252 
7253     //! Check if a path is a file.
7254     /**
7255        \param path Specified path to test.
7256     **/
7257     inline bool is_file(const char *const path) {
7258       if (!path || !*path) return false;
7259 #if cimg_OS==2
7260       const DWORD res = cimg::win_getfileattributes(path);
7261       return res!=INVALID_FILE_ATTRIBUTES && !(res&FILE_ATTRIBUTE_DIRECTORY);
7262 #else
7263       std::FILE *const file = cimg::std_fopen(path,"rb");
7264       if (!file) return false;
7265       cimg::fclose(file);
7266       return !is_directory(path);
7267 #endif
7268     }
7269 
7270     //! Get file size.
7271     /**
7272        \param filename Specified filename to get size from.
7273        \return File size or '-1' if file does not exist.
7274     **/
7275     inline cimg_int64 fsize(const char *const filename) {
7276       std::FILE *const file = cimg::std_fopen(filename,"rb");
7277       if (!file) return (cimg_int64)-1;
7278       std::fseek(file,0,SEEK_END);
7279       const cimg_int64 siz = (cimg_int64)std::ftell(file);
7280       cimg::fclose(file);
7281       return siz;
7282     }
7283 
7284     //! Get last write time of a given file or directory (multiple-attributes version).
7285     /**
7286        \param path Specified path to get attributes from.
7287        \param[in,out] attr Type of requested time attributes.
7288                       Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
7289                       Replaced by read attributes after return (or -1 if an error occurred).
7290        \param nb_attr Number of attributes to read/write.
7291        \return Latest read attribute.
7292     **/
7293     template<typename T>
7294     inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) {
7295 #define _cimg_fdate_err() for (unsigned int i = 0; i<nb_attr; ++i) attr[i] = (T)-1
7296       int res = -1;
7297       if (!path || !*path) { _cimg_fdate_err(); return -1; }
7298       cimg::mutex(6);
7299 #if cimg_OS==2
7300       HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
7301       if (file!=INVALID_HANDLE_VALUE) {
7302         FILETIME _ft;
7303         SYSTEMTIME ft;
7304         if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) {
7305           for (unsigned int i = 0; i<nb_attr; ++i) {
7306             res = (int)(attr[i]==0?ft.wYear:attr[i]==1?ft.wMonth:attr[i]==2?ft.wDay:
7307                         attr[i]==3?ft.wDayOfWeek:attr[i]==4?ft.wHour:attr[i]==5?ft.wMinute:
7308                         attr[i]==6?ft.wSecond:-1);
7309             attr[i] = (T)res;
7310           }
7311         } else _cimg_fdate_err();
7312         CloseHandle(file);
7313       } else _cimg_fdate_err();
7314 #elif cimg_OS==1
7315       struct stat st_buf;
7316       if (!stat(path,&st_buf)) {
7317         const time_t _ft = st_buf.st_mtime;
7318         const struct tm& ft = *std::localtime(&_ft);
7319         for (unsigned int i = 0; i<nb_attr; ++i) {
7320           res = (int)(attr[i]==0?ft.tm_year + 1900:attr[i]==1?ft.tm_mon + 1:attr[i]==2?ft.tm_mday:
7321                       attr[i]==3?ft.tm_wday:attr[i]==4?ft.tm_hour:attr[i]==5?ft.tm_min:
7322                       attr[i]==6?ft.tm_sec:-1);
7323           attr[i] = (T)res;
7324         }
7325       } else _cimg_fdate_err();
7326 #endif
7327       cimg::mutex(6,0);
7328       return res;
7329     }
7330 
7331     //! Get last write time of a given file or directory (single-attribute version).
7332     /**
7333        \param path Specified path to get attributes from.
7334        \param attr Type of requested time attributes.
7335                    Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
7336        \return Specified attribute or -1 if an error occurred.
7337     **/
7338     inline int fdate(const char *const path, unsigned int attr) {
7339       int out = (int)attr;
7340       return fdate(path,&out,1);
7341     }
7342 
7343     //! Get current local time (multiple-attributes version).
7344     /**
7345        \param[in,out] attr Type of requested time attributes.
7346                            Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second |
7347                                     7=millisecond }
7348                            Replaced by read attributes after return (or -1 if an error occurred).
7349        \param nb_attr Number of attributes to read/write.
7350        \return Latest read attribute.
7351     **/
7352     template<typename T>
7353     inline int date(T *attr, const unsigned int nb_attr) {
7354       int res = -1;
7355       cimg::mutex(6);
7356 #if cimg_OS==2
7357       SYSTEMTIME st;
7358       GetLocalTime(&st);
7359       for (unsigned int i = 0; i<nb_attr; ++i) {
7360         res = (int)(attr[i]==0?st.wYear:
7361                     attr[i]==1?st.wMonth:
7362                     attr[i]==2?st.wDay:
7363                     attr[i]==3?st.wDayOfWeek:
7364                     attr[i]==4?st.wHour:
7365                     attr[i]==5?st.wMinute:
7366                     attr[i]==6?st.wSecond:
7367                     attr[i]==7?st.wMilliseconds:-1);
7368         attr[i] = (T)res;
7369       }
7370 #else
7371       struct timeval _st;
7372       gettimeofday(&_st,0);
7373       struct tm *st = std::localtime(&_st.tv_sec);
7374       for (unsigned int i = 0; i<nb_attr; ++i) {
7375         res = (int)(attr[i]==0?st->tm_year + 1900:
7376                     attr[i]==1?st->tm_mon + 1:
7377                     attr[i]==2?st->tm_mday:
7378                     attr[i]==3?st->tm_wday:
7379                     attr[i]==4?st->tm_hour:
7380                     attr[i]==5?st->tm_min:
7381                     attr[i]==6?st->tm_sec:
7382                     attr[i]==7?_st.tv_usec/1000:-1);
7383         attr[i] = (T)res;
7384       }
7385 #endif
7386       cimg::mutex(6,0);
7387       return res;
7388     }
7389 
7390     //! Get current local time (single-attribute version).
7391     /**
7392        \param attr Type of requested time attribute.
7393                    Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second |
7394                             7=millisecond }
7395        \return Specified attribute or -1 if an error occurred.
7396     **/
7397     inline int date(unsigned int attr) {
7398       int out = (int)attr;
7399       return date(&out,1);
7400     }
7401 
7402     // Get/set path to the \c curl binary.
7403     inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false);
7404 
7405     // Get/set path to the \c dcraw binary.
7406     inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false);
7407 
7408     // Get/set path to the FFMPEG's \c ffmpeg binary.
7409     inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false);
7410 
7411     // Get/set path to the GraphicsMagick's \c gm binary.
7412     inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false);
7413 
7414     // Get/set path to the \c gunzip binary.
7415     inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false);
7416 
7417     // Get/set path to the \c gzip binary.
7418     inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false);
7419 
7420     // Get/set path to the ImageMagick's \c convert binary.
7421     inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false);
7422 
7423     // Get/set path to the Medcon's \c medcon binary.
7424     inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false);
7425 
7426     // Get/set path to store temporary files.
7427     inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false);
7428 
7429     // Get/set path to the \c wget binary.
7430     inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false);
7431 
7432     //! Split filename into two C-strings \c body and \c extension.
7433     /**
7434        filename and body must not overlap!
7435     **/
7436     inline const char *split_filename(const char *const filename, char *const body=0) {
7437       if (!filename) { if (body) *body = 0; return ""; }
7438       const char * p = std::strrchr(filename,'.');
7439       if (!p || std::strchr(p,'/') || std::strchr(p,'\\')) { // No extension.
7440         if (body) std::strcpy(body,filename);
7441         return filename + std::strlen(filename);
7442       }
7443       const unsigned int l = (unsigned int)(p - filename);
7444       if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; }
7445       return p + 1;
7446     }
7447 
7448     // Generate a numbered version of a filename.
7449     inline char* number_filename(const char *const filename, const int number,
7450                                  const unsigned int digits, char *const str);
7451 
7452     //! Read data from file.
7453     /**
7454        \param[out] ptr Pointer to memory buffer that will contain the binary data read from file.
7455        \param nmemb Number of elements to read.
7456        \param stream File to read data from.
7457        \return Number of read elements.
7458        \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read.
7459     **/
7460     template<typename T>
7461     inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) {
7462       if (!ptr || !stream)
7463         throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
7464                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
7465       if (!nmemb) return 0;
7466       const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
7467       size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
7468       do {
7469         l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
7470         l_al_read = std::fread((void*)(ptr + al_read),sizeof(T),l_to_read,stream);
7471         al_read+=l_al_read;
7472         to_read-=l_al_read;
7473       } while (l_to_read==l_al_read && to_read>0);
7474       if (to_read>0)
7475         warn("cimg::fread(): Only %lu/%lu elements could be read from file.",
7476              (unsigned long)al_read,(unsigned long)nmemb);
7477       return al_read;
7478     }
7479 
7480     //! Write data to file.
7481     /**
7482        \param ptr Pointer to memory buffer containing the binary data to write on file.
7483        \param nmemb Number of elements to write.
7484        \param[out] stream File to write data on.
7485        \return Number of written elements.
7486        \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written.
7487     **/
7488     template<typename T>
7489     inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) {
7490       if (!ptr || !stream)
7491         throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
7492                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
7493       if (!nmemb) return 0;
7494       const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
7495       size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
7496       do {
7497         l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
7498         l_al_write = std::fwrite((void*)(ptr + al_write),sizeof(T),l_to_write,stream);
7499         al_write+=l_al_write;
7500         to_write-=l_al_write;
7501       } while (l_to_write==l_al_write && to_write>0);
7502       if (to_write>0)
7503         warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.",
7504              (unsigned long)al_write,(unsigned long)nmemb);
7505       return al_write;
7506     }
7507 
7508     //! Create an empty file.
7509     /**
7510        \param file Input file (can be \c 0 if \c filename is set).
7511        \param filename Filename, as a C-string (can be \c 0 if \c file is set).
7512     **/
7513     inline void fempty(std::FILE *const file, const char *const filename) {
7514       if (!file && !filename)
7515         throw CImgArgumentException("cimg::fempty(): Specified filename is (null).");
7516       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
7517       if (!file) cimg::fclose(nfile);
7518     }
7519 
7520     // Try to guess format from an image file.
7521     inline const char *ftype(std::FILE *const file, const char *const filename);
7522 
7523     // Get or set load from network mode (can be { 0=disabled | 1=enabled }).
7524     inline bool& network_mode(const bool value, const bool is_set) {
7525       static bool mode = true;
7526       if (is_set) { cimg::mutex(0); mode = value; cimg::mutex(0,0); }
7527       return mode;
7528     }
7529 
7530     inline bool& network_mode() {
7531       return network_mode(false,false);
7532     }
7533 
7534     // Load file from network as a local temporary file.
7535     inline char *load_network(const char *const url, char *const filename_local,
7536                               const unsigned int timeout=0, const bool try_fallback=false,
7537                               const char *const referer=0);
7538 
7539     //! Return options specified on the command line.
7540     inline const char* option(const char *const name, const int argc, const char *const *const argv,
7541                               const char *const _default, const char *const usage, const bool reset_static) {
7542       static bool first = true, visu = false;
7543       if (reset_static) { first = true; return 0; }
7544       const char *res = 0;
7545       if (first) {
7546         first = false;
7547         visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
7548         visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
7549         visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
7550       }
7551       if (!name && visu) {
7552         if (usage) {
7553           std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
7554           std::fprintf(cimg::output(),": %s",usage);
7555           std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time);
7556         }
7557         if (_default) std::fprintf(cimg::output(),"%s\n",_default);
7558       }
7559       if (name) {
7560         if (argc>0) {
7561           int k = 0;
7562           while (k<argc && std::strcmp(argv[k],name)) ++k;
7563           res = (k++==argc?_default:(k==argc?argv[--k]:argv[k]));
7564         } else res = _default;
7565         if (visu && usage) std::fprintf(cimg::output(),"    %s%-16s%s %-24s %s%s%s\n",
7566                                         cimg::t_bold,name,cimg::t_normal,res?res:"0",
7567                                         cimg::t_green,usage,cimg::t_normal);
7568       }
7569       return res;
7570     }
7571 
7572     inline const char* option(const char *const name, const int argc, const char *const *const argv,
7573                               const char *const _default, const char *const usage=0) {
7574       return option(name,argc,argv,_default,usage,false);
7575     }
7576 
7577     inline bool option(const char *const name, const int argc, const char *const *const argv,
7578                        const bool _default, const char *const usage=0) {
7579       const char *const s = cimg::option(name,argc,argv,(char*)0);
7580       const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):_default;
7581       cimg::option(name,0,0,res?"true":"false",usage);
7582       return res;
7583     }
7584 
7585     inline int option(const char *const name, const int argc, const char *const *const argv,
7586                       const int _default, const char *const usage=0) {
7587       const char *const s = cimg::option(name,argc,argv,(char*)0);
7588       const int res = s?std::atoi(s):_default;
7589       char *const tmp = new char[256];
7590       cimg_snprintf(tmp,256,"%d",res);
7591       cimg::option(name,0,0,tmp,usage);
7592       delete[] tmp;
7593       return res;
7594     }
7595 
7596     inline char option(const char *const name, const int argc, const char *const *const argv,
7597                        const char _default, const char *const usage=0) {
7598       const char *const s = cimg::option(name,argc,argv,(char*)0);
7599       const char res = s?*s:_default;
7600       char tmp[8];
7601       *tmp = res; tmp[1] = 0;
7602       cimg::option(name,0,0,tmp,usage);
7603       return res;
7604     }
7605 
7606     inline float option(const char *const name, const int argc, const char *const *const argv,
7607                         const float _default, const char *const usage=0) {
7608       const char *const s = cimg::option(name,argc,argv,(char*)0);
7609       const float res = s?(float)cimg::atof(s):_default;
7610       char *const tmp = new char[256];
7611       cimg_snprintf(tmp,256,"%g",res);
7612       cimg::option(name,0,0,tmp,usage);
7613       delete[] tmp;
7614       return res;
7615     }
7616 
7617     inline double option(const char *const name, const int argc, const char *const *const argv,
7618                          const double _default, const char *const usage=0) {
7619       const char *const s = cimg::option(name,argc,argv,(char*)0);
7620       const double res = s?cimg::atof(s):_default;
7621       char *const tmp = new char[256];
7622       cimg_snprintf(tmp,256,"%g",res);
7623       cimg::option(name,0,0,tmp,usage);
7624       delete[] tmp;
7625       return res;
7626     }
7627 
7628     //! Print information about \CImg environment variables.
7629     /**
7630        \note Output is done on the default output stream.
7631     **/
7632     inline void info() {
7633       std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
7634                    cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
7635                    cimg::t_normal,cimg_date,cimg_time);
7636 
7637       std::fprintf(cimg::output(),"  > Operating System:         %s%-13s%s %s('cimg_OS'=%d)%s\n",
7638                    cimg::t_bold,
7639                    cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknown"),
7640                    cimg::t_normal,cimg::t_green,
7641                    cimg_OS,
7642                    cimg::t_normal);
7643 
7644       std::fprintf(cimg::output(),"  > CPU endianness:           %s%s Endian%s\n",
7645                    cimg::t_bold,
7646                    cimg::endianness()?"Big":"Little",
7647                    cimg::t_normal);
7648 
7649       std::fprintf(cimg::output(),"  > Verbosity mode:           %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
7650                    cimg::t_bold,
7651                    cimg_verbosity==0?"Quiet":
7652                    cimg_verbosity==1?"Console":
7653                    cimg_verbosity==2?"Dialog":
7654                    cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings",
7655                    cimg::t_normal,cimg::t_green,
7656                    cimg_verbosity,
7657                    cimg::t_normal);
7658 
7659       std::fprintf(cimg::output(),"  > Stricts warnings:         %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
7660                    cimg::t_bold,
7661 #ifdef cimg_strict_warnings
7662                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7663 #else
7664                    "No",cimg::t_normal,cimg::t_green,"undefined",
7665 #endif
7666                    cimg::t_normal);
7667 
7668       std::fprintf(cimg::output(),"  > Support for C++11:        %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n",
7669                    cimg::t_bold,
7670                    cimg_use_cpp11?"Yes":"No",
7671                    cimg::t_normal,cimg::t_green,
7672                    (int)cimg_use_cpp11,
7673                    cimg::t_normal);
7674 
7675       std::fprintf(cimg::output(),"  > Using VT100 messages:     %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
7676                    cimg::t_bold,
7677 #ifdef cimg_use_vt100
7678                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7679 #else
7680                    "No",cimg::t_normal,cimg::t_green,"undefined",
7681 #endif
7682                    cimg::t_normal);
7683 
7684       std::fprintf(cimg::output(),"  > Display type:             %s%-13s%s %s('cimg_display'=%d)%s\n",
7685                    cimg::t_bold,
7686                    cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
7687                    cimg::t_normal,cimg::t_green,
7688                    (int)cimg_display,
7689                    cimg::t_normal);
7690 
7691 #if cimg_display==1
7692       std::fprintf(cimg::output(),"  > Using XShm for X11:       %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
7693                    cimg::t_bold,
7694 #ifdef cimg_use_xshm
7695                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7696 #else
7697                    "No",cimg::t_normal,cimg::t_green,"undefined",
7698 #endif
7699                    cimg::t_normal);
7700 
7701       std::fprintf(cimg::output(),"  > Using XRand for X11:      %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
7702                    cimg::t_bold,
7703 #ifdef cimg_use_xrandr
7704                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7705 #else
7706                    "No",cimg::t_normal,cimg::t_green,"undefined",
7707 #endif
7708                    cimg::t_normal);
7709 #endif
7710       std::fprintf(cimg::output(),"  > Using OpenMP:             %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
7711                    cimg::t_bold,
7712 #if cimg_use_openmp!=0
7713                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7714 #else
7715                    "No",cimg::t_normal,cimg::t_green,"undefined",
7716 #endif
7717                    cimg::t_normal);
7718       std::fprintf(cimg::output(),"  > Using PNG library:        %s%-13s%s %s('cimg_use_png' %s)%s\n",
7719                    cimg::t_bold,
7720 #ifdef cimg_use_png
7721                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7722 #else
7723                    "No",cimg::t_normal,cimg::t_green,"undefined",
7724 #endif
7725                    cimg::t_normal);
7726       std::fprintf(cimg::output(),"  > Using JPEG library:       %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
7727                    cimg::t_bold,
7728 #ifdef cimg_use_jpeg
7729                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7730 #else
7731                    "No",cimg::t_normal,cimg::t_green,"undefined",
7732 #endif
7733                    cimg::t_normal);
7734 
7735       std::fprintf(cimg::output(),"  > Using TIFF library:       %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
7736                    cimg::t_bold,
7737 #ifdef cimg_use_tiff
7738                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7739 #else
7740                    "No",cimg::t_normal,cimg::t_green,"undefined",
7741 #endif
7742                    cimg::t_normal);
7743 
7744       std::fprintf(cimg::output(),"  > Using Magick++ library:   %s%-13s%s %s('cimg_use_magick' %s)%s\n",
7745                    cimg::t_bold,
7746 #ifdef cimg_use_magick
7747                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7748 #else
7749                    "No",cimg::t_normal,cimg::t_green,"undefined",
7750 #endif
7751                    cimg::t_normal);
7752 
7753       std::fprintf(cimg::output(),"  > Using FFTW3 library:      %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
7754                    cimg::t_bold,
7755 #ifdef cimg_use_fftw3
7756                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7757 #else
7758                    "No",cimg::t_normal,cimg::t_green,"undefined",
7759 #endif
7760                    cimg::t_normal);
7761 
7762       std::fprintf(cimg::output(),"  > Using LAPACK library:     %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
7763                    cimg::t_bold,
7764 #ifdef cimg_use_lapack
7765                    "Yes",cimg::t_normal,cimg::t_green,"defined",
7766 #else
7767                    "No",cimg::t_normal,cimg::t_green,"undefined",
7768 #endif
7769                    cimg::t_normal);
7770 
7771       char *const tmp = new char[1024];
7772 
7773       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::curl_path());
7774       std::fprintf(cimg::output(),"  > Path of 'curl':           %s%-13s%s\n",
7775                    cimg::t_bold,
7776                    tmp,
7777                    cimg::t_normal);
7778 
7779       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::dcraw_path());
7780       std::fprintf(cimg::output(),"  > Path of 'dcraw':          %s%-13s%s\n",
7781                    cimg::t_bold,
7782                    tmp,
7783                    cimg::t_normal);
7784 
7785       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::ffmpeg_path());
7786       std::fprintf(cimg::output(),"  > Path of 'ffmpeg':         %s%-13s%s\n",
7787                    cimg::t_bold,
7788                    tmp,
7789                    cimg::t_normal);
7790 
7791       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path());
7792       std::fprintf(cimg::output(),"  > Path of 'graphicsmagick': %s%-13s%s\n",
7793                    cimg::t_bold,
7794                    tmp,
7795                    cimg::t_normal);
7796 
7797       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gunzip_path());
7798       std::fprintf(cimg::output(),"  > Path of 'gunzip':         %s%-13s%s\n",
7799                    cimg::t_bold,
7800                    tmp,
7801                    cimg::t_normal);
7802 
7803       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gzip_path());
7804       std::fprintf(cimg::output(),"  > Path of 'gzip':           %s%-13s%s\n",
7805                    cimg::t_bold,
7806                    tmp,
7807                    cimg::t_normal);
7808 
7809       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path());
7810       std::fprintf(cimg::output(),"  > Path of 'imagemagick':    %s%-13s%s\n",
7811                    cimg::t_bold,
7812                    tmp,
7813                    cimg::t_normal);
7814 
7815       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path());
7816       std::fprintf(cimg::output(),"  > Path of 'medcon':         %s%-13s%s\n",
7817                    cimg::t_bold,
7818                    tmp,
7819                    cimg::t_normal);
7820 
7821       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path());
7822       std::fprintf(cimg::output(),"  > Temporary path:           %s%-13s%s\n",
7823                    cimg::t_bold,
7824                    tmp,
7825                    cimg::t_normal);
7826 
7827       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::wget_path());
7828       std::fprintf(cimg::output(),"  > Path of 'wget':           %s%-13s%s\n",
7829                    cimg::t_bold,
7830                    tmp,
7831                    cimg::t_normal);
7832 
7833       std::fprintf(cimg::output(),"\n");
7834       delete[] tmp;
7835     }
7836 
7837     // Declare LAPACK function signatures if LAPACK support is enabled.
7838 #ifdef cimg_use_lapack
7839     template<typename T>
7840     inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
7841       dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
7842     }
7843 
7844     inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
7845       sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
7846     }
7847 
7848     template<typename T>
7849     inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
7850       dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
7851     }
7852 
7853     inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
7854       sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
7855     }
7856 
7857     template<typename T>
7858     inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
7859                       T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
7860       dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
7861     }
7862 
7863     inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
7864                       float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
7865       sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
7866     }
7867 
7868     template<typename T>
7869     inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
7870       int one = 1;
7871       dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
7872     }
7873 
7874     inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
7875       int one = 1;
7876       sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
7877     }
7878 
7879     template<typename T>
7880     inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
7881       dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
7882     }
7883 
7884     inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
7885       ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
7886     }
7887 
7888     template<typename T>
7889     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
7890                       T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO) {
7891       dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
7892     }
7893 
7894     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
7895                       float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO) {
7896       sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
7897     }
7898 
7899 #endif
7900 
7901   } // namespace cimg { ...
7902 
7903   /*------------------------------------------------
7904    #
7905    #
7906    #   Definition of mathematical operators and
7907    #   external functions.
7908    #
7909    #
7910    -------------------------------------------------*/
7911 
7912 #define _cimg_create_operator(typ) \
7913   template<typename T> \
7914   inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
7915     return img + val; \
7916   } \
7917   template<typename T> \
7918   inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
7919     typedef typename cimg::superset<T,typ>::type Tt; \
7920     return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
7921   } \
7922   template<typename T> \
7923   inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
7924     return img*val; \
7925   } \
7926   template<typename T> \
7927   inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
7928     return val*img.get_invert(); \
7929   } \
7930   template<typename T> \
7931   inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
7932     return img & val; \
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 img ^ val; \
7941   } \
7942   template<typename T> \
7943   inline bool operator==(const typ val, const CImg<T>& img) {   \
7944     return img == val; \
7945   } \
7946   template<typename T> \
7947   inline bool operator!=(const typ val, const CImg<T>& img) { \
7948     return img != val; \
7949   }
7950 
7951   _cimg_create_operator(bool)
7952   _cimg_create_operator(unsigned char)
7953   _cimg_create_operator(char)
7954   _cimg_create_operator(signed char)
7955   _cimg_create_operator(unsigned short)
7956   _cimg_create_operator(short)
7957   _cimg_create_operator(unsigned int)
7958   _cimg_create_operator(int)
7959   _cimg_create_operator(cimg_uint64)
7960   _cimg_create_operator(cimg_int64)
7961   _cimg_create_operator(float)
7962   _cimg_create_operator(double)
7963   _cimg_create_operator(long double)
7964 
7965   template<typename T>
7966   inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
7967     return img + expression;
7968   }
7969 
7970   template<typename T>
7971   inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
7972     return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img;
7973   }
7974 
7975   template<typename T>
7976   inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
7977     return img*expression;
7978   }
7979 
7980   template<typename T>
7981   inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
7982     return expression*img.get_invert();
7983   }
7984 
7985   template<typename T>
7986   inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
7987     return img & expression;
7988   }
7989 
7990   template<typename T>
7991   inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
7992     return img | expression;
7993   }
7994 
7995   template<typename T>
7996   inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
7997     return img ^ expression;
7998   }
7999 
8000   template<typename T>
8001   inline bool operator==(const char *const expression, const CImg<T>& img) {
8002     return img==expression;
8003   }
8004 
8005   template<typename T>
8006   inline bool operator!=(const char *const expression, const CImg<T>& img) {
8007     return img!=expression;
8008   }
8009 
8010   template<typename T>
8011   inline CImg<T> transpose(const CImg<T>& instance) {
8012     return instance.get_transpose();
8013   }
8014 
8015   template<typename T>
8016   inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance, const bool use_LU=true) {
8017     return instance.get_invert(use_LU);
8018   }
8019 
8020   template<typename T>
8021   inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance, const bool use_LU=false) {
8022     return instance.get_pseudoinvert(use_LU);
8023   }
8024 
8025 #define _cimg_create_pointwise_function(name) \
8026   template<typename T> \
8027   inline CImg<_cimg_Tfloat> name(const CImg<T>& instance) { \
8028     return instance.get_##name(); \
8029   }
8030 
8031   _cimg_create_pointwise_function(sqr)
8032   _cimg_create_pointwise_function(sqrt)
8033   _cimg_create_pointwise_function(exp)
8034   _cimg_create_pointwise_function(log)
8035   _cimg_create_pointwise_function(log2)
8036   _cimg_create_pointwise_function(log10)
8037   _cimg_create_pointwise_function(abs)
8038   _cimg_create_pointwise_function(sign)
8039   _cimg_create_pointwise_function(cos)
8040   _cimg_create_pointwise_function(sin)
8041   _cimg_create_pointwise_function(sinc)
8042   _cimg_create_pointwise_function(tan)
8043   _cimg_create_pointwise_function(acos)
8044   _cimg_create_pointwise_function(asin)
8045   _cimg_create_pointwise_function(atan)
8046   _cimg_create_pointwise_function(cosh)
8047   _cimg_create_pointwise_function(sinh)
8048   _cimg_create_pointwise_function(tanh)
8049   _cimg_create_pointwise_function(acosh)
8050   _cimg_create_pointwise_function(asinh)
8051   _cimg_create_pointwise_function(atanh)
8052 
8053   /*-----------------------------------
8054    #
8055    # Define the CImgDisplay structure
8056    #
8057    ----------------------------------*/
8058   //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events).
8059   /**
8060      CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window
8061      (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems).
8062      If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter
8063      a minimal mode where warning messages will be outputted each time the program is trying to call one of the
8064      CImgDisplay method.
8065 
8066      The configuration variable \c cimg_display tells about the graphic library used.
8067      It is set automatically by \CImg when one of these graphic libraries has been detected.
8068      But, you can override its value if necessary. Valid choices are:
8069      - 0: Disable display capabilities.
8070      - 1: Use \b X-Window (X11) library.
8071      - 2: Use \b GDI32 library.
8072 
8073      Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay.
8074   **/
8075   struct CImgDisplay {
8076     cimg_uint64 _timer, _fps_frames, _fps_timer;
8077     unsigned int _width, _height, _normalization;
8078     float _fps_fps, _min, _max;
8079     bool _is_fullscreen;
8080     char *_title;
8081     unsigned int _window_width, _window_height, _button, *_keys, *_released_keys;
8082     int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
8083     bool _is_closed, _is_resized, _is_moved, _is_event,
8084       _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
8085       _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
8086       _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
8087       _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
8088       _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
8089       _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
8090       _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
8091       _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
8092       _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT,
8093       _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3,
8094       _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB,
8095       _is_keyPADMUL, _is_keyPADDIV;
8096 
8097     //@}
8098     //---------------------------
8099     //
8100     //! \name Plugins
8101     //@{
8102     //---------------------------
8103 
8104 #ifdef cimgdisplay_plugin
8105 #include cimgdisplay_plugin
8106 #endif
8107 #ifdef cimgdisplay_plugin1
8108 #include cimgdisplay_plugin1
8109 #endif
8110 #ifdef cimgdisplay_plugin2
8111 #include cimgdisplay_plugin2
8112 #endif
8113 #ifdef cimgdisplay_plugin3
8114 #include cimgdisplay_plugin3
8115 #endif
8116 #ifdef cimgdisplay_plugin4
8117 #include cimgdisplay_plugin4
8118 #endif
8119 #ifdef cimgdisplay_plugin5
8120 #include cimgdisplay_plugin5
8121 #endif
8122 #ifdef cimgdisplay_plugin6
8123 #include cimgdisplay_plugin6
8124 #endif
8125 #ifdef cimgdisplay_plugin7
8126 #include cimgdisplay_plugin7
8127 #endif
8128 #ifdef cimgdisplay_plugin8
8129 #include cimgdisplay_plugin8
8130 #endif
8131 
8132     //@}
8133     //--------------------------------------------------------
8134     //
8135     //! \name Constructors / Destructor / Instance Management
8136     //@{
8137     //--------------------------------------------------------
8138 
8139     //! Destructor.
8140     /**
8141        \note If the associated window is visible on the screen, it is closed by the call to the destructor.
8142     **/
8143     ~CImgDisplay() {
8144       assign();
8145       delete[] _keys;
8146       delete[] _released_keys;
8147     }
8148 
8149     //! Construct an empty display.
8150     /**
8151        \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until
8152        display of valid data is performed.
8153        \par Example
8154        \code
8155        CImgDisplay disp;  // Does actually nothing
8156        ...
8157        disp.display(img); // Construct new window and display image in it
8158        \endcode
8159     **/
8160     CImgDisplay():
8161       _width(0),_height(0),_normalization(0),
8162       _min(0),_max(0),
8163       _is_fullscreen(false),
8164       _title(0),
8165       _window_width(0),_window_height(0),_button(0),
8166       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8167       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8168       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8169       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8170       assign();
8171     }
8172 
8173     //! Construct a display with specified dimensions.
8174     /** \param width Window width.
8175         \param height Window height.
8176         \param title Window title.
8177         \param normalization Normalization type
8178         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
8179         \param is_fullscreen Tells if fullscreen mode is enabled.
8180         \param is_closed Tells if associated window is initially visible or not.
8181         \note A black background is initially displayed on the associated window.
8182     **/
8183     CImgDisplay(const unsigned int width, const unsigned int height,
8184                 const char *const title=0, const unsigned int normalization=3,
8185                 const bool is_fullscreen=false, const bool is_closed=false):
8186       _width(0),_height(0),_normalization(0),
8187       _min(0),_max(0),
8188       _is_fullscreen(false),
8189       _title(0),
8190       _window_width(0),_window_height(0),_button(0),
8191       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8192       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8193       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8194       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8195       assign(width,height,title,normalization,is_fullscreen,is_closed);
8196     }
8197 
8198     //! Construct a display from an image.
8199     /** \param img Image used as a model to create the window.
8200         \param title Window title.
8201         \param normalization Normalization type
8202         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
8203         \param is_fullscreen Tells if fullscreen mode is enabled.
8204         \param is_closed Tells if associated window is initially visible or not.
8205         \note The pixels of the input image are initially displayed on the associated window.
8206     **/
8207     template<typename T>
8208     explicit CImgDisplay(const CImg<T>& img,
8209                          const char *const title=0, const unsigned int normalization=3,
8210                          const bool is_fullscreen=false, const bool is_closed=false):
8211       _width(0),_height(0),_normalization(0),
8212       _min(0),_max(0),
8213       _is_fullscreen(false),
8214       _title(0),
8215       _window_width(0),_window_height(0),_button(0),
8216       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8217       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8218       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8219       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8220       assign(img,title,normalization,is_fullscreen,is_closed);
8221     }
8222 
8223     //! Construct a display from an image list.
8224     /** \param list The images list to display.
8225         \param title Window title.
8226         \param normalization Normalization type
8227         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
8228         \param is_fullscreen Tells if fullscreen mode is enabled.
8229         \param is_closed Tells if associated window is initially visible or not.
8230         \note All images of the list, appended along the X-axis, are initially displayed on the associated window.
8231     **/
8232     template<typename T>
8233     explicit CImgDisplay(const CImgList<T>& list,
8234                          const char *const title=0, const unsigned int normalization=3,
8235                          const bool is_fullscreen=false, const bool is_closed=false):
8236       _width(0),_height(0),_normalization(0),
8237       _min(0),_max(0),
8238       _is_fullscreen(false),
8239       _title(0),
8240       _window_width(0),_window_height(0),_button(0),
8241       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8242       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8243       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8244       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8245       assign(list,title,normalization,is_fullscreen,is_closed);
8246     }
8247 
8248     //! Construct a display as a copy of an existing one.
8249     /**
8250         \param disp Display instance to copy.
8251         \note The pixel buffer of the input window is initially displayed on the associated window.
8252     **/
8253     CImgDisplay(const CImgDisplay& disp):
8254       _width(0),_height(0),_normalization(0),
8255       _min(0),_max(0),
8256       _is_fullscreen(false),
8257       _title(0),
8258       _window_width(0),_window_height(0),_button(0),
8259       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
8260       _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
8261       _mouse_x(-1),_mouse_y(-1),_wheel(0),
8262       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
8263       assign(disp);
8264     }
8265 
8266     //! Take a screenshot.
8267     /**
8268        \param[out] img Output screenshot. Can be empty on input
8269     **/
8270     template<typename T>
8271     static void screenshot(CImg<T>& img) {
8272       return screenshot(0,0,cimg::type<int>::max(),cimg::type<int>::max(),img);
8273     }
8274 
8275 #if cimg_display==0
8276 
8277     static void _no_display_exception() {
8278       throw CImgDisplayException("CImgDisplay(): No display available.");
8279     }
8280 
8281     //! Destructor - Empty constructor \inplace.
8282     /**
8283        \note Replace the current instance by an empty display.
8284     **/
8285     CImgDisplay& assign() {
8286       return flush();
8287     }
8288 
8289     //! Construct a display with specified dimensions \inplace.
8290     /**
8291     **/
8292     CImgDisplay& assign(const unsigned int width, const unsigned int height,
8293                         const char *const title=0, const unsigned int normalization=3,
8294                         const bool is_fullscreen=false, const bool is_closed=false) {
8295       cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
8296       _no_display_exception();
8297       return assign();
8298     }
8299 
8300     //! Construct a display from an image \inplace.
8301     /**
8302     **/
8303     template<typename T>
8304     CImgDisplay& assign(const CImg<T>& img,
8305                         const char *const title=0, const unsigned int normalization=3,
8306                         const bool is_fullscreen=false, const bool is_closed=false) {
8307       _no_display_exception();
8308       return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
8309     }
8310 
8311     //! Construct a display from an image list \inplace.
8312     /**
8313     **/
8314     template<typename T>
8315     CImgDisplay& assign(const CImgList<T>& list,
8316                         const char *const title=0, const unsigned int normalization=3,
8317                         const bool is_fullscreen=false, const bool is_closed=false) {
8318       _no_display_exception();
8319       return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
8320     }
8321 
8322     //! Construct a display as a copy of another one \inplace.
8323     /**
8324     **/
8325     CImgDisplay& assign(const CImgDisplay &disp) {
8326       _no_display_exception();
8327       return assign(disp._width,disp._height);
8328     }
8329 
8330 #endif
8331 
8332     //! Return a reference to an empty display.
8333     /**
8334        \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&)
8335        must have a default value.
8336        \par Example
8337        \code
8338        void foo(CImgDisplay& disp=CImgDisplay::empty());
8339        \endcode
8340     **/
8341     static CImgDisplay& empty() {
8342       static CImgDisplay _empty;
8343       return _empty.assign();
8344     }
8345 
8346     //! Return a reference to an empty display \const.
8347     static const CImgDisplay& const_empty() {
8348       static const CImgDisplay _empty;
8349       return _empty;
8350     }
8351 
8352 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,false), \
8353                                  CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,true)
8354     static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
8355                                    const int dmin, const int dmax, const bool return_y) {
8356       const int
8357         u = CImgDisplay::screen_width(),
8358         v = CImgDisplay::screen_height();
8359       const float
8360         mw = dmin<0?cimg::round(u*-dmin/100.f):(float)dmin,
8361         mh = dmin<0?cimg::round(v*-dmin/100.f):(float)dmin,
8362         Mw = dmax<0?cimg::round(u*-dmax/100.f):(float)dmax,
8363         Mh = dmax<0?cimg::round(v*-dmax/100.f):(float)dmax;
8364       float
8365         w = (float)std::max(1U,dx),
8366         h = (float)std::max(1U,dy);
8367       if (dz>1) { w+=dz; h+=dz; }
8368       if (w<mw) { h = h*mw/w; w = mw; }
8369       if (h<mh) { w = w*mh/h; h = mh; }
8370       if (w>Mw) { h = h*Mw/w; w = Mw; }
8371       if (h>Mh) { w = w*Mh/h; h = Mh; }
8372       if (w<mw) w = mw;
8373       if (h<mh) h = mh;
8374       return std::max(1U,(unsigned int)cimg::round(return_y?h:w));
8375     }
8376 
8377     //@}
8378     //------------------------------------------
8379     //
8380     //! \name Overloaded Operators
8381     //@{
8382     //------------------------------------------
8383 
8384     //! Display image on associated window.
8385     /**
8386        \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>.
8387     **/
8388     template<typename t>
8389     CImgDisplay& operator=(const CImg<t>& img) {
8390       return display(img);
8391     }
8392 
8393     //! Display list of images on associated window.
8394     /**
8395        \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>.
8396     **/
8397     template<typename t>
8398     CImgDisplay& operator=(const CImgList<t>& list) {
8399       return display(list);
8400     }
8401 
8402     //! Construct a display as a copy of another one \inplace.
8403     /**
8404        \note Equivalent to assign(const CImgDisplay&).
8405      **/
8406     CImgDisplay& operator=(const CImgDisplay& disp) {
8407       return assign(disp);
8408     }
8409 
8410     //! Return \c false if display is empty, \c true otherwise.
8411     /**
8412        \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>.
8413     **/
8414     operator bool() const {
8415       return !is_empty();
8416     }
8417 
8418     //@}
8419     //------------------------------------------
8420     //
8421     //! \name Instance Checking
8422     //@{
8423     //------------------------------------------
8424 
8425     //! Return \c true if display is empty, \c false otherwise.
8426     /**
8427     **/
8428     bool is_empty() const {
8429       return !(_width && _height);
8430     }
8431 
8432     //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise.
8433     /**
8434        \note
8435        - When a user physically closes the associated window, the display is set to closed.
8436        - A closed display is not destroyed. Its associated window can be show again on the screen using show().
8437     **/
8438     bool is_closed() const {
8439       return _is_closed;
8440     }
8441 
8442     //! Return \c true if associated window has been resized on the screen, \c false otherwise.
8443     /**
8444     **/
8445     bool is_resized() const {
8446       return _is_resized;
8447     }
8448 
8449     //! Return \c true if associated window has been moved on the screen, \c false otherwise.
8450     /**
8451     **/
8452     bool is_moved() const {
8453       return _is_moved;
8454     }
8455 
8456     //! Return \c true if any event has occurred on the associated window, \c false otherwise.
8457     /**
8458     **/
8459     bool is_event() const {
8460       return _is_event;
8461     }
8462 
8463     //! Return \c true if current display is in fullscreen mode, \c false otherwise.
8464     /**
8465     **/
8466     bool is_fullscreen() const {
8467       return _is_fullscreen;
8468     }
8469 
8470     //! Return \c true if any key is being pressed on the associated window, \c false otherwise.
8471     /**
8472        \note The methods below do the same only for specific keys.
8473     **/
8474     bool is_key() const {
8475       return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
8476         _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
8477         _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
8478         _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
8479         _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
8480         _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
8481         _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
8482         _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
8483         _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
8484         _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
8485         _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
8486         _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
8487         _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
8488         _is_keyK || _is_keyL || _is_keyENTER ||
8489         _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
8490         _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
8491         _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
8492         _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
8493         _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
8494         _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
8495         _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
8496         _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
8497         _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
8498         _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
8499         _is_keyPADMUL || _is_keyPADDIV;
8500     }
8501 
8502     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
8503     /**
8504        \param keycode Keycode to test.
8505        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8506        your code stay portable (see cimg::keyESC).
8507        \par Example
8508        \code
8509        CImgDisplay disp(400,400);
8510        while (!disp.is_closed()) {
8511          if (disp.key(cimg::keyTAB)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'
8512          disp.wait();
8513        }
8514        \endcode
8515     **/
8516     bool is_key(const unsigned int keycode) const {
8517 #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
8518       _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
8519       _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
8520       _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
8521       _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
8522       _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
8523       _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
8524       _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
8525       _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
8526       _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
8527       _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
8528       _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
8529       _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
8530       _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
8531       _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
8532       _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
8533       _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
8534       _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
8535       _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
8536       _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
8537       _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
8538       _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
8539       _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
8540       _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
8541       _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
8542       _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
8543       return false;
8544     }
8545 
8546     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
8547     /**
8548        \param keycode C-string containing the keycode label of the key to test.
8549        \note Use it when the key you want to test can be dynamically set by the user.
8550        \par Example
8551        \code
8552        CImgDisplay disp(400,400);
8553        const char *const keycode = "TAB";
8554        while (!disp.is_closed()) {
8555          if (disp.is_key(keycode)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'
8556          disp.wait();
8557        }
8558        \endcode
8559     **/
8560     bool& is_key(const char *const keycode) {
8561       static bool f = false;
8562       f = false;
8563 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
8564       _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
8565       _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
8566       _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
8567       _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
8568       _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
8569       _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
8570       _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
8571       _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
8572       _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
8573       _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
8574       _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
8575       _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
8576       _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
8577       _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
8578       _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
8579       _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
8580       _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
8581       _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
8582       _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
8583       _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
8584       _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
8585       _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
8586       _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
8587       _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
8588       _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
8589       return f;
8590     }
8591 
8592     //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise.
8593     /**
8594        \param keycodes_sequence Buffer of keycodes to test.
8595        \param length Number of keys in the \c keycodes_sequence buffer.
8596        \param remove_sequence Tells if the key sequence must be removed from the key history, if found.
8597        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8598        your code stay portable (see cimg::keyESC).
8599        \par Example
8600        \code
8601        CImgDisplay disp(400,400);
8602        const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD };
8603        while (!disp.is_closed()) {
8604          if (disp.is_key_sequence(key_seq,2)) { ... }  // Test for the 'CTRL+D' keyboard event
8605          disp.wait();
8606        }
8607        \endcode
8608     **/
8609     bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length,
8610                          const bool remove_sequence=false) {
8611       if (keycodes_sequence && length) {
8612         const unsigned int
8613           *const ps_end = keycodes_sequence + length - 1,
8614           *const pk_end = (unsigned int*)_keys + 1 + 128 - length,
8615           k = *ps_end;
8616         for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
8617           if (*(pk++)==k) {
8618             bool res = true;
8619             const unsigned int *ps = ps_end, *pk2 = pk;
8620             for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
8621             if (res) {
8622               if (remove_sequence) std::memset((void*)(pk - 1),0,sizeof(unsigned int)*length);
8623               return true;
8624             }
8625           }
8626         }
8627       }
8628       return false;
8629     }
8630 
8631 #define _cimg_iskey_def(k) \
8632     bool is_key##k() const { \
8633       return _is_key##k; \
8634     }
8635 
8636     //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise.
8637     /**
8638        \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC).
8639     **/
8640     _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
8641     _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
8642     _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
8643     _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
8644     _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
8645     _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
8646     _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
8647     _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
8648     _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
8649     _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
8650     _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
8651     _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
8652     _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
8653     _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
8654     _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
8655     _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
8656     _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
8657     _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
8658     _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
8659     _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
8660     _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
8661     _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
8662     _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
8663     _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
8664     _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
8665 
8666     //@}
8667     //------------------------------------------
8668     //
8669     //! \name Instance Characteristics
8670     //@{
8671     //------------------------------------------
8672 
8673 #if cimg_display==0
8674 
8675     //! Return width of the screen (current resolution along the X-axis).
8676     /**
8677     **/
8678     static int screen_width() {
8679       _no_display_exception();
8680       return 0;
8681     }
8682 
8683     //! Return height of the screen (current resolution along the Y-axis).
8684     /**
8685     **/
8686     static int screen_height() {
8687       _no_display_exception();
8688       return 0;
8689     }
8690 
8691 #endif
8692 
8693     //! Return display width.
8694     /**
8695        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
8696        may be different from the actual width of the associated window.
8697     **/
8698     int width() const {
8699       return (int)_width;
8700     }
8701 
8702     //! Return display height.
8703     /**
8704        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
8705        may be different from the actual height of the associated window.
8706     **/
8707     int height() const {
8708       return (int)_height;
8709     }
8710 
8711     //! Return normalization type of the display.
8712     /**
8713        The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be
8714        correctly displayed. The range of values for pixels displayed on screen is <tt>[0,255]</tt>.
8715        If the range of values of the data to display is different, a normalization may be required for displaying
8716        the data in a correct way. The normalization type can be one of:
8717        - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the
8718        CImgDisplay instance have values in range <tt>[0,255]</tt>.
8719        - \c 1: Value normalization is always performed (this is the default behavior).
8720        Before displaying an input image, its values will be (virtually) stretched
8721        in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum.
8722        Use this mode for images whose minimum and maximum values are not prescribed to known values
8723        (e.g. float-valued images).
8724        Note that when normalized versions of images are computed for display purposes, the actual values of these
8725        images are not modified.
8726        - \c 2: Value normalization is performed once (on the first image display), then the same normalization
8727        coefficients are kept for next displayed frames.
8728        - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types,
8729        the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then
8730        for <tt>unsigned char</tt>).
8731        For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image
8732        data instead.
8733     **/
8734     unsigned int normalization() const {
8735       return _normalization;
8736     }
8737 
8738     //! Return title of the associated window as a C-string.
8739     /**
8740        \note Window title may be not visible, depending on the used window manager or if the current display is
8741        in fullscreen mode.
8742     **/
8743     const char *title() const {
8744       return _title?_title:"";
8745     }
8746 
8747     //! Return width of the associated window.
8748     /**
8749        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
8750        may be different from the actual width of the associated window.
8751     **/
8752     int window_width() const {
8753       return (int)_window_width;
8754     }
8755 
8756     //! Return height of the associated window.
8757     /**
8758        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
8759        may be different from the actual height of the associated window.
8760     **/
8761     int window_height() const {
8762       return (int)_window_height;
8763     }
8764 
8765     //! Return X-coordinate of the associated window.
8766     /**
8767        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
8768     **/
8769     int window_x() const {
8770       return _window_x;
8771     }
8772 
8773     //! Return Y-coordinate of the associated window.
8774     /**
8775        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
8776     **/
8777     int window_y() const {
8778       return _window_y;
8779     }
8780 
8781     //! Return X-coordinate of the mouse pointer.
8782     /**
8783        \note
8784        - If the mouse pointer is outside window area, \c -1 is returned.
8785        - Otherwise, the returned value is in the range [0,width()-1].
8786     **/
8787     int mouse_x() const {
8788       return _mouse_x;
8789     }
8790 
8791     //! Return Y-coordinate of the mouse pointer.
8792     /**
8793        \note
8794        - If the mouse pointer is outside window area, \c -1 is returned.
8795        - Otherwise, the returned value is in the range [0,height()-1].
8796     **/
8797     int mouse_y() const {
8798       return _mouse_y;
8799     }
8800 
8801     //! Return current state of the mouse buttons.
8802     /**
8803        \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned
8804        value is set:
8805        - bit \c 0 (value \c 0x1): State of the left mouse button.
8806        - bit \c 1 (value \c 0x2): State of the right mouse button.
8807        - bit \c 2 (value \c 0x4): State of the middle mouse button.
8808 
8809        Several bits can be activated if more than one button are pressed at the same time.
8810        \par Example
8811        \code
8812        CImgDisplay disp(400,400);
8813        while (!disp.is_closed()) {
8814          if (disp.button()&1) { // Left button clicked
8815            ...
8816          }
8817          if (disp.button()&2) { // Right button clicked
8818            ...
8819          }
8820          if (disp.button()&4) { // Middle button clicked
8821            ...
8822          }
8823          disp.wait();
8824        }
8825        \endcode
8826     **/
8827     unsigned int button() const {
8828       return _button;
8829     }
8830 
8831     //! Return current state of the mouse wheel.
8832     /**
8833        \note
8834        - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled
8835        forward or backward.
8836        - Scrolling the wheel forward add \c 1 to the wheel value.
8837        - Scrolling the wheel backward subtract \c 1 to the wheel value.
8838        - The returned value cumulates the number of forward of backward scrolls since the creation of the display,
8839        or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset
8840        the wheel counter when an action has been performed regarding the current wheel value.
8841        Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done
8842        (as many in forward as in backward directions).
8843        \par Example
8844        \code
8845        CImgDisplay disp(400,400);
8846        while (!disp.is_closed()) {
8847          if (disp.wheel()) {
8848            int counter = disp.wheel();  // Read the state of the mouse wheel
8849            ...                          // Do what you want with 'counter'
8850            disp.set_wheel();            // Reset the wheel value to 0
8851          }
8852          disp.wait();
8853        }
8854        \endcode
8855     **/
8856     int wheel() const {
8857       return _wheel;
8858     }
8859 
8860     //! Return one entry from the pressed keys history.
8861     /**
8862        \param pos Index to read from the pressed keys history (index \c 0 corresponds to latest entry).
8863        \return Keycode of a pressed key or \c 0 for a released key.
8864        \note
8865        - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed,
8866        its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead.
8867        This means that up to the 64 last pressed keys may be read from the pressed keys history.
8868        When a new value is stored, the pressed keys history is shifted so that the latest entry is always
8869        stored at position \c 0.
8870        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8871        your code stay portable (see cimg::keyESC).
8872     **/
8873     unsigned int& key(const unsigned int pos=0) const {
8874       static unsigned int key0;
8875       return pos<128?_keys[pos]:(key0 = 0);
8876 
8877     }
8878 
8879     //! Return one entry from the released keys history.
8880     /**
8881        \param pos Index to read from the released keys history (index \c 0 corresponds to latest entry).
8882        \return Keycode of a released key or \c 0 for a pressed key.
8883        \note
8884        - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released,
8885        its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead.
8886        This means that up to the 64 last released keys may be read from the released keys history.
8887        When a new value is stored, the released keys history is shifted so that the latest entry is always
8888        stored at position \c 0.
8889        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8890        your code stay portable (see cimg::keyESC).
8891     **/
8892     unsigned int& released_key(const unsigned int pos=0) const {
8893       static unsigned int key0;
8894       return pos<128?_released_keys[pos]:(key0 = 0);
8895     }
8896 
8897     //! Return keycode corresponding to the specified string.
8898     /**
8899        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8900        your code stay portable (see cimg::keyESC).
8901        \par Example
8902        \code
8903        const unsigned int keyTAB = CImgDisplay::keycode("TAB");  // Return cimg::keyTAB
8904        \endcode
8905     **/
8906     static unsigned int keycode(const char *const keycode) {
8907 #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
8908       _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
8909       _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
8910       _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
8911       _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
8912       _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
8913       _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
8914       _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
8915       _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
8916       _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
8917       _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
8918       _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
8919       _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
8920       _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
8921       _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
8922       _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
8923       _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
8924       _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
8925       _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
8926       _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
8927       _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
8928       _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
8929       _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
8930       _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
8931       _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
8932       _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
8933       return 0;
8934     }
8935 
8936     //! Return the current refresh rate, in frames per second.
8937     /**
8938        \note Returns a significant value when the current instance is used to display successive frames.
8939        It measures the delay between successive calls to frames_per_second().
8940     **/
8941     float frames_per_second() {
8942       if (!_fps_timer) _fps_timer = cimg::time();
8943       const float delta = (float)((cimg::time() - _fps_timer)/1000.f);
8944       ++_fps_frames;
8945       if (delta>=1) {
8946         _fps_fps = _fps_frames/delta;
8947         _fps_frames = 0;
8948         _fps_timer = cimg::time();
8949       }
8950       return _fps_fps;
8951     }
8952 
8953     // Move current display window so that its content stays inside the current screen.
8954     CImgDisplay& move_inside_screen() {
8955       if (is_empty()) return *this;
8956       const int
8957         x0 = window_x(),
8958         y0 = window_y(),
8959         x1 = x0 + window_width() - 1,
8960         y1 = y0 + window_height() - 1,
8961         sw = CImgDisplay::screen_width(),
8962         sh = CImgDisplay::screen_height();
8963       if (x0<0 || y0<0 || x1>=sw || y1>=sh)
8964         move(std::max(0,std::min(x0,sw - x1 + x0)),
8965              std::max(0,std::min(y0,sh - y1 + y0)));
8966       return *this;
8967     }
8968 
8969     //@}
8970     //---------------------------------------
8971     //
8972     //! \name Window Manipulation
8973     //@{
8974     //---------------------------------------
8975 
8976 #if cimg_display==0
8977 
8978     //! Display image on associated window.
8979     /**
8980        \param img Input image to display.
8981        \note This method returns immediately.
8982     **/
8983     template<typename T>
8984     CImgDisplay& display(const CImg<T>& img) {
8985       return assign(img);
8986     }
8987 
8988 #endif
8989 
8990     //! Display list of images on associated window.
8991     /**
8992        \param list List of images to display.
8993        \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c).
8994        \param align Relative position of aligned images when displaying lists with images of different sizes
8995        (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right).
8996        \note This method returns immediately.
8997     **/
8998     template<typename T>
8999     CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
9000       if (list._width==1) {
9001         const CImg<T>& img = list[0];
9002         if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img);
9003       }
9004       CImgList<typename CImg<T>::ucharT> visu(list._width);
9005       unsigned int dims = 0;
9006       cimglist_for(list,l) {
9007         const CImg<T>& img = list._data[l];
9008         img._get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2,
9009                         (img._depth - 1)/2).move_to(visu[l]);
9010         dims = std::max(dims,visu[l]._spectrum);
9011       }
9012       cimglist_for(list,l) if (visu[l]._spectrum<dims) visu[l].resize(-100,-100,-100,dims,1);
9013       visu.get_append(axis,align).display(*this);
9014       return *this;
9015     }
9016 
9017 #if cimg_display==0
9018 
9019     //! Show (closed) associated window on the screen.
9020     /**
9021        \note
9022        - Force the associated window of a display to be visible on the screen, even if it has been closed before.
9023        - Using show() on a visible display does nothing.
9024     **/
9025     CImgDisplay& show() {
9026       return assign();
9027     }
9028 
9029     //! Close (visible) associated window and make it disappear from the screen.
9030     /**
9031        \note
9032        - A closed display only means the associated window is not visible anymore. This does not mean the display has
9033        been destroyed.
9034        Use show() to make the associated window reappear.
9035        - Using close() on a closed display does nothing.
9036     **/
9037     CImgDisplay& close() {
9038       return assign();
9039     }
9040 
9041     //! Move associated window to a new location.
9042     /**
9043        \param pos_x X-coordinate of the new window location.
9044        \param pos_y Y-coordinate of the new window location.
9045        \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown
9046        nevertheless).
9047     **/
9048     CImgDisplay& move(const int pos_x, const int pos_y) {
9049       return assign(pos_x,pos_y);
9050     }
9051 
9052 #endif
9053 
9054     //! Resize display to the size of the associated window.
9055     /**
9056        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
9057        \note
9058        - Calling this method ensures that width() and window_width() become equal, as well as height() and
9059        window_height().
9060        - The associated window is also resized to specified dimensions.
9061     **/
9062     CImgDisplay& resize(const bool force_redraw=true) {
9063       resize(window_width(),window_height(),force_redraw);
9064       return *this;
9065     }
9066 
9067 #if cimg_display==0
9068 
9069     //! Resize display to the specified size.
9070     /**
9071        \param width Requested display width.
9072        \param height Requested display height.
9073        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
9074        \note The associated window is also resized to specified dimensions.
9075     **/
9076     CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
9077       return assign(width,height,0,3,force_redraw);
9078     }
9079 
9080 #endif
9081 
9082     //! Resize display to the size of an input image.
9083     /**
9084        \param img Input image to take size from.
9085        \param force_redraw Tells if the previous window content must be resized and updated as well.
9086        \note
9087        - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and
9088        <tt>img.height()</tt>.
9089        - The associated window is also resized to specified dimensions.
9090     **/
9091     template<typename T>
9092     CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
9093       return resize(img._width,img._height,force_redraw);
9094     }
9095 
9096     //! Resize display to the size of another CImgDisplay instance.
9097     /**
9098        \param disp Input display to take size from.
9099        \param force_redraw Tells if the previous window content must be resized and updated as well.
9100        \note
9101        - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and
9102        <tt>disp.height()</tt>.
9103        - The associated window is also resized to specified dimensions.
9104     **/
9105     CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
9106       return resize(disp.width(),disp.height(),force_redraw);
9107     }
9108 
9109     // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
9110     template<typename t, typename T>
9111     static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
9112                                t *ptrd, const unsigned int wd, const unsigned int hd) {
9113       typedef typename cimg::last<T,cimg_ulong>::type ulongT;
9114       const ulongT one = (ulongT)1;
9115       CImg<ulongT> off_x(wd), off_y(hd + 1);
9116       if (wd==ws) off_x.fill(1);
9117       else {
9118         ulongT *poff_x = off_x._data, curr = 0;
9119         for (unsigned int x = 0; x<wd; ++x) {
9120           const ulongT old = curr;
9121           curr = (x + one)*ws/wd;
9122           *(poff_x++) = curr - old;
9123         }
9124       }
9125       if (hd==hs) off_y.fill(ws);
9126       else {
9127         ulongT *poff_y = off_y._data, curr = 0;
9128         for (unsigned int y = 0; y<hd; ++y) {
9129           const ulongT old = curr;
9130           curr = (y + one)*hs/hd;
9131           *(poff_y++) = ws*(curr - old);
9132         }
9133         *poff_y = 0;
9134       }
9135       ulongT *poff_y = off_y._data;
9136       for (unsigned int y = 0; y<hd; ) {
9137         const T *ptr = ptrs;
9138         ulongT *poff_x = off_x._data;
9139         for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poff_x++); }
9140         ++y;
9141         ulongT dy = *(poff_y++);
9142         for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poff_y++)) {}
9143         ptrs+=dy;
9144       }
9145     }
9146 
9147     //! Set normalization type.
9148     /**
9149        \param normalization New normalization mode.
9150     **/
9151     CImgDisplay& set_normalization(const unsigned int normalization) {
9152       _normalization = normalization;
9153       _min = _max = 0;
9154       return *this;
9155     }
9156 
9157 #if cimg_display==0
9158 
9159     //! Set title of the associated window.
9160     /**
9161        \param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
9162        \warning As the first argument is a format string, it is highly recommended to write
9163        \code
9164        disp.set_title("%s",window_title);
9165        \endcode
9166        instead of
9167        \code
9168        disp.set_title(window_title);
9169        \endcode
9170        if \c window_title can be arbitrary, to prevent nasty memory access.
9171     **/
9172     CImgDisplay& set_title(const char *const format, ...) {
9173       return assign(0,0,format);
9174     }
9175 
9176 #endif
9177 
9178     //! Enable or disable fullscreen mode.
9179     /**
9180        \param is_fullscreen Tells is the fullscreen mode must be activated or not.
9181        \param force_redraw Tells if the previous window content must be displayed as well.
9182        \note
9183        - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the
9184        current display is not modified.
9185        - The screen resolution may be switched to fit the associated window size and ensure it appears the largest
9186        as possible.
9187        For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen
9188        resolution change (requires the X11 extensions to be enabled).
9189     **/
9190     CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
9191       if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
9192       return toggle_fullscreen(force_redraw);
9193     }
9194 
9195 #if cimg_display==0
9196 
9197     //! Toggle fullscreen mode.
9198     /**
9199        \param force_redraw Tells if the previous window content must be displayed as well.
9200        \note Enable fullscreen mode if it was not enabled, and disable it otherwise.
9201     **/
9202     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
9203       return assign(_width,_height,0,3,force_redraw);
9204     }
9205 
9206     //! Show mouse pointer.
9207     /**
9208        \note Depending on the window manager behavior, this method may not succeed
9209        (no exceptions are thrown nevertheless).
9210     **/
9211     CImgDisplay& show_mouse() {
9212       return assign();
9213     }
9214 
9215     //! Hide mouse pointer.
9216     /**
9217        \note Depending on the window manager behavior, this method may not succeed
9218        (no exceptions are thrown nevertheless).
9219     **/
9220     CImgDisplay& hide_mouse() {
9221       return assign();
9222     }
9223 
9224     //! Move mouse pointer to a specified location.
9225     /**
9226        \note Depending on the window manager behavior, this method may not succeed
9227        (no exceptions are thrown nevertheless).
9228     **/
9229     CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
9230       return assign(pos_x,pos_y);
9231     }
9232 
9233 #endif
9234 
9235     //! Simulate a mouse button release event.
9236     /**
9237        \note All mouse buttons are considered released at the same time.
9238     **/
9239     CImgDisplay& set_button() {
9240       _button = 0;
9241       _is_event = true;
9242 #if cimg_display==1
9243       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9244 #elif cimg_display==2
9245       SetEvent(cimg::Win32_attr().wait_event);
9246 #endif
9247       return *this;
9248     }
9249 
9250     //! Simulate a mouse button press or release event.
9251     /**
9252        \param button Buttons event code, where each button is associated to a single bit.
9253        \param is_pressed Tells if the mouse button is considered as pressed or released.
9254     **/
9255     CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
9256       const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U;
9257       if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
9258       _is_event = buttoncode?true:false;
9259       if (buttoncode) {
9260 #if cimg_display==1
9261         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9262 #elif cimg_display==2
9263         SetEvent(cimg::Win32_attr().wait_event);
9264 #endif
9265       }
9266       return *this;
9267     }
9268 
9269     //! Flush all mouse wheel events.
9270     /**
9271        \note Make wheel() to return \c 0, if called afterwards.
9272     **/
9273     CImgDisplay& set_wheel() {
9274       _wheel = 0;
9275       _is_event = true;
9276 #if cimg_display==1
9277       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9278 #elif cimg_display==2
9279       SetEvent(cimg::Win32_attr().wait_event);
9280 #endif
9281       return *this;
9282     }
9283 
9284     //! Simulate a wheel event.
9285     /**
9286        \param amplitude Amplitude of the wheel scrolling to simulate.
9287        \note Make wheel() to return \c amplitude, if called afterwards.
9288     **/
9289     CImgDisplay& set_wheel(const int amplitude) {
9290       _wheel+=amplitude;
9291       _is_event = amplitude?true:false;
9292       if (amplitude) {
9293 #if cimg_display==1
9294         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9295 #elif cimg_display==2
9296         SetEvent(cimg::Win32_attr().wait_event);
9297 #endif
9298       }
9299       return *this;
9300     }
9301 
9302     //! Flush all key events.
9303     /**
9304        \note Make key() to return \c 0, if called afterwards.
9305     **/
9306     CImgDisplay& set_key() {
9307       std::memset((void*)_keys,0,128*sizeof(unsigned int));
9308       std::memset((void*)_released_keys,0,128*sizeof(unsigned int));
9309       _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
9310         _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
9311         _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
9312         _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
9313         _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
9314         _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
9315         _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
9316         _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
9317         _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
9318         _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
9319         _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
9320         _is_keyPADDIV = false;
9321       _is_event = true;
9322 #if cimg_display==1
9323       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9324 #elif cimg_display==2
9325       SetEvent(cimg::Win32_attr().wait_event);
9326 #endif
9327       return *this;
9328     }
9329 
9330     //! Simulate a keyboard press/release event.
9331     /**
9332        \param keycode Keycode of the associated key.
9333        \param is_pressed Tells if the key is considered as pressed or released.
9334        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
9335        your code stay portable (see cimg::keyESC).
9336     **/
9337     CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
9338 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
9339       _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
9340       _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
9341       _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
9342       _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
9343       _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
9344       _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
9345       _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
9346       _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
9347       _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
9348       _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
9349       _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
9350       _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
9351       _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
9352       _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
9353       _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
9354       _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
9355       _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
9356       _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
9357       _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
9358       _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
9359       _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
9360       _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
9361       _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
9362       _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
9363       _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
9364       if (is_pressed) {
9365         if (*_keys)
9366           std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
9367         *_keys = keycode;
9368         if (*_released_keys) {
9369           std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
9370           *_released_keys = 0;
9371         }
9372       } else {
9373         if (*_keys) {
9374           std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
9375           *_keys = 0;
9376         }
9377         if (*_released_keys)
9378           std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
9379         *_released_keys = keycode;
9380       }
9381       _is_event = keycode?true:false;
9382       if (keycode) {
9383 #if cimg_display==1
9384         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9385 #elif cimg_display==2
9386         SetEvent(cimg::Win32_attr().wait_event);
9387 #endif
9388       }
9389       return *this;
9390     }
9391 
9392     //! Flush all display events.
9393     /**
9394        \note Remove all passed events from the current display.
9395     **/
9396     CImgDisplay& flush() {
9397       set_key().set_button().set_wheel();
9398       _is_resized = _is_moved = _is_event = false;
9399       _fps_timer = _fps_frames = _timer = 0;
9400       _fps_fps = 0;
9401       return *this;
9402     }
9403 
9404     //! Wait for any user event occurring on the current display.
9405     CImgDisplay& wait() {
9406       wait(*this);
9407       return *this;
9408     }
9409 
9410     //! Wait for a given number of milliseconds since the last call to wait().
9411     /**
9412        \param milliseconds Number of milliseconds to wait for.
9413        \note Similar to cimg::wait().
9414     **/
9415     CImgDisplay& wait(const unsigned int milliseconds) {
9416       cimg::wait(milliseconds,&_timer);
9417       return *this;
9418     }
9419 
9420     //! Wait for any event occurring on the display \c disp1.
9421     static void wait(CImgDisplay& disp1) {
9422       disp1._is_event = false;
9423       while (!disp1._is_closed && !disp1._is_event) wait_all();
9424     }
9425 
9426     //! Wait for any event occurring either on the display \c disp1 or \c disp2.
9427     static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
9428       disp1._is_event = disp2._is_event = false;
9429       while ((!disp1._is_closed || !disp2._is_closed) &&
9430              !disp1._is_event && !disp2._is_event) wait_all();
9431     }
9432 
9433     //! Wait for any event occurring either on the display \c disp1, \c disp2 or \c disp3.
9434     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
9435       disp1._is_event = disp2._is_event = disp3._is_event = false;
9436       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
9437              !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
9438     }
9439 
9440     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
9441     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
9442       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
9443       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
9444              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
9445     }
9446 
9447     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
9448     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
9449                      CImgDisplay& disp5) {
9450       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
9451       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
9452              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
9453         wait_all();
9454     }
9455 
9456     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
9457     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9458                      CImgDisplay& disp6) {
9459       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9460         disp6._is_event = false;
9461       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9462               !disp6._is_closed) &&
9463              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9464              !disp6._is_event) wait_all();
9465     }
9466 
9467     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
9468     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9469                      CImgDisplay& disp6, CImgDisplay& disp7) {
9470       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9471         disp6._is_event = disp7._is_event = false;
9472       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9473               !disp6._is_closed || !disp7._is_closed) &&
9474              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9475              !disp6._is_event && !disp7._is_event) wait_all();
9476     }
9477 
9478     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
9479     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9480                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
9481       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9482         disp6._is_event = disp7._is_event = disp8._is_event = false;
9483       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9484               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
9485              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9486              !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
9487     }
9488 
9489     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
9490     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9491                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
9492       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9493         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
9494       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9495               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
9496              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9497              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
9498     }
9499 
9500     //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
9501     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
9502                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
9503                      CImgDisplay& disp10) {
9504       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
9505         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
9506       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
9507               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
9508              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
9509              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
9510         wait_all();
9511     }
9512 
9513 #if cimg_display==0
9514 
9515     //! Wait for any window event occurring in any opened CImgDisplay.
9516     static void wait_all() {
9517       return _no_display_exception();
9518     }
9519 
9520     //! Render image into internal display buffer.
9521     /**
9522        \param img Input image data to render.
9523        \note
9524        - Convert image data representation into the internal display buffer (architecture-dependent structure).
9525        - The content of the associated window is not modified, until paint() is called.
9526        - Should not be used for common CImgDisplay uses, since display() is more useful.
9527     **/
9528     template<typename T>
9529     CImgDisplay& render(const CImg<T>& img) {
9530       return assign(img);
9531     }
9532 
9533     //! Paint internal display buffer on associated window.
9534     /**
9535        \note
9536        - Update the content of the associated window with the internal display buffer, e.g. after a render() call.
9537        - Should not be used for common CImgDisplay uses, since display() is more useful.
9538     **/
9539     CImgDisplay& paint() {
9540       return assign();
9541     }
9542 
9543 
9544     //! Take a snapshot of the current screen content.
9545     /**
9546        \param x0 X-coordinate of the upper left corner.
9547        \param y0 Y-coordinate of the upper left corner.
9548        \param x1 X-coordinate of the lower right corner.
9549        \param y1 Y-coordinate of the lower right corner.
9550        \param[out] img Output screenshot. Can be empty on input
9551     **/
9552     template<typename T>
9553     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
9554       cimg::unused(x0,y0,x1,y1,&img);
9555       _no_display_exception();
9556     }
9557 
9558     //! Take a snapshot of the associated window content.
9559     /**
9560        \param[out] img Output snapshot. Can be empty on input.
9561     **/
9562     template<typename T>
9563     const CImgDisplay& snapshot(CImg<T>& img) const {
9564       cimg::unused(img);
9565       _no_display_exception();
9566       return *this;
9567     }
9568 #endif
9569 
9570     // X11-based implementation
9571     //--------------------------
9572 #if cimg_display==1
9573 
9574     Atom _wm_window_atom, _wm_protocol_atom;
9575     Window _window, _background_window;
9576     Colormap _colormap;
9577     XImage *_image;
9578     void *_data;
9579 
9580 #ifdef cimg_use_xshm
9581     XShmSegmentInfo *_shminfo;
9582 #endif
9583 
9584     static int screen_width() {
9585       Display *const dpy = cimg::X11_attr().display;
9586       int res = 0;
9587       if (!dpy) {
9588         Display *const _dpy = XOpenDisplay(0);
9589         if (!_dpy)
9590           throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
9591         res = DisplayWidth(_dpy,DefaultScreen(_dpy));
9592         XCloseDisplay(_dpy);
9593       } else {
9594 
9595 #ifdef cimg_use_xrandr
9596         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
9597           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
9598         else res = DisplayWidth(dpy,DefaultScreen(dpy));
9599 #else
9600         res = DisplayWidth(dpy,DefaultScreen(dpy));
9601 #endif
9602       }
9603       return res;
9604     }
9605 
9606     static int screen_height() {
9607       Display *const dpy = cimg::X11_attr().display;
9608       int res = 0;
9609       if (!dpy) {
9610         Display *const _dpy = XOpenDisplay(0);
9611         if (!_dpy)
9612           throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
9613         res = DisplayHeight(_dpy,DefaultScreen(_dpy));
9614         XCloseDisplay(_dpy);
9615       } else {
9616 
9617 #ifdef cimg_use_xrandr
9618         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
9619           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
9620         else res = DisplayHeight(dpy,DefaultScreen(dpy));
9621 #else
9622         res = DisplayHeight(dpy,DefaultScreen(dpy));
9623 #endif
9624       }
9625       return res;
9626     }
9627 
9628     static void wait_all() {
9629       if (!cimg::X11_attr().display) return;
9630       pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex);
9631       pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
9632       pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex);
9633     }
9634 
9635     void _handle_events(const XEvent *const pevent) {
9636       Display *const dpy = cimg::X11_attr().display;
9637       XEvent event = *pevent;
9638       switch (event.type) {
9639       case ClientMessage : {
9640         if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
9641             (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
9642           XUnmapWindow(cimg::X11_attr().display,_window);
9643           _is_closed = _is_event = true;
9644           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9645         }
9646       } break;
9647       case ConfigureNotify : {
9648         while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
9649         const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
9650         const int nx = event.xconfigure.x, ny = event.xconfigure.y;
9651         if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
9652           _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
9653           XResizeWindow(dpy,_window,_window_width,_window_height);
9654           _is_resized = _is_event = true;
9655           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9656         }
9657         if (nx!=_window_x || ny!=_window_y) {
9658           _window_x = nx;
9659           _window_y = ny;
9660           _is_moved = _is_event = true;
9661           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9662         }
9663       } break;
9664       case Expose : {
9665         while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
9666         _paint(false);
9667         if (_is_fullscreen) {
9668           XWindowAttributes attr;
9669           do {
9670             XGetWindowAttributes(dpy,_window,&attr);
9671             if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
9672           } while (attr.map_state!=IsViewable);
9673           XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
9674         }
9675       } break;
9676       case ButtonPress : {
9677         do {
9678           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
9679           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
9680           switch (event.xbutton.button) {
9681           case 1 : set_button(1); break;
9682           case 3 : set_button(2); break;
9683           case 2 : set_button(3); break;
9684           }
9685         } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
9686       } break;
9687       case ButtonRelease : {
9688         do {
9689           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
9690           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
9691           switch (event.xbutton.button) {
9692           case 1 : set_button(1,false); break;
9693           case 3 : set_button(2,false); break;
9694           case 2 : set_button(3,false); break;
9695           case 4 : set_wheel(1); break;
9696           case 5 : set_wheel(-1); break;
9697           }
9698         } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
9699       } break;
9700       case KeyPress : {
9701         char tmp = 0; KeySym ksym;
9702         XLookupString(&event.xkey,&tmp,1,&ksym,0);
9703         set_key((unsigned int)ksym,true);
9704       } break;
9705       case KeyRelease : {
9706         char keys_return[32];  // Check that the key has been physically unpressed
9707         XQueryKeymap(dpy,keys_return);
9708         const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
9709         const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
9710         if (!is_key_pressed) {
9711           char tmp = 0; KeySym ksym;
9712           XLookupString(&event.xkey,&tmp,1,&ksym,0);
9713           set_key((unsigned int)ksym,false);
9714         }
9715       } break;
9716       case EnterNotify: {
9717         while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
9718         _mouse_x = event.xmotion.x;
9719         _mouse_y = event.xmotion.y;
9720         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
9721       } break;
9722       case LeaveNotify : {
9723         while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
9724         _mouse_x = _mouse_y = -1; _is_event = true;
9725         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9726       } break;
9727       case MotionNotify : {
9728         while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
9729         _mouse_x = event.xmotion.x;
9730         _mouse_y = event.xmotion.y;
9731         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
9732         _is_event = true;
9733         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
9734       } break;
9735       }
9736     }
9737 
9738     static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows
9739       Display *const dpy = cimg::X11_attr().display;
9740       XEvent event;
9741       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
9742       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
9743       if (!arg) for ( ; ; ) {
9744         cimg_lock_display();
9745         bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
9746         if (!event_flag) event_flag = XCheckMaskEvent(dpy,
9747                                                       ExposureMask | StructureNotifyMask | ButtonPressMask |
9748                                                       KeyPressMask | PointerMotionMask | EnterWindowMask |
9749                                                       LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
9750         if (event_flag)
9751           for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
9752             if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
9753               cimg::X11_attr().wins[i]->_handle_events(&event);
9754         cimg_unlock_display();
9755         pthread_testcancel();
9756         cimg::sleep(8);
9757       }
9758       return 0;
9759     }
9760 
9761     void _set_colormap(Colormap& cmap, const unsigned int dim) {
9762       XColor *const colormap = new XColor[256];
9763       switch (dim) {
9764       case 1 : { // colormap for greyscale images
9765         for (unsigned int index = 0; index<256; ++index) {
9766           colormap[index].pixel = index;
9767           colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
9768           colormap[index].flags = DoRed | DoGreen | DoBlue;
9769         }
9770       } break;
9771       case 2 : { // colormap for RG images
9772         for (unsigned int index = 0, r = 8; r<256; r+=16)
9773           for (unsigned int g = 8; g<256; g+=16) {
9774             colormap[index].pixel = index;
9775             colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
9776             colormap[index].green = (unsigned short)(g<<8);
9777             colormap[index++].flags = DoRed | DoGreen | DoBlue;
9778           }
9779       } break;
9780       default : { // colormap for RGB images
9781         for (unsigned int index = 0, r = 16; r<256; r+=32)
9782           for (unsigned int g = 16; g<256; g+=32)
9783             for (unsigned int b = 32; b<256; b+=64) {
9784               colormap[index].pixel = index;
9785               colormap[index].red = (unsigned short)(r<<8);
9786               colormap[index].green = (unsigned short)(g<<8);
9787               colormap[index].blue = (unsigned short)(b<<8);
9788               colormap[index++].flags = DoRed | DoGreen | DoBlue;
9789             }
9790       }
9791       }
9792       XStoreColors(cimg::X11_attr().display,cmap,colormap,256);
9793       delete[] colormap;
9794     }
9795 
9796     void _map_window() {
9797       Display *const dpy = cimg::X11_attr().display;
9798       bool is_exposed = false, is_mapped = false;
9799       XWindowAttributes attr;
9800       XEvent event;
9801       XMapRaised(dpy,_window);
9802       do { // Wait for the window to be mapped
9803         XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
9804         switch (event.type) {
9805         case MapNotify : is_mapped = true; break;
9806         case Expose : is_exposed = true; break;
9807         }
9808       } while (!is_exposed || !is_mapped);
9809       do { // Wait for the window to be visible
9810         XGetWindowAttributes(dpy,_window,&attr);
9811         if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
9812       } while (attr.map_state!=IsViewable);
9813       _window_x = attr.x;
9814       _window_y = attr.y;
9815     }
9816 
9817     void _paint(const bool wait_expose=true) {
9818       if (_is_closed || !_image) return;
9819       Display *const dpy = cimg::X11_attr().display;
9820       if (wait_expose) { // Send an expose event sticked to display window to force repaint
9821         XEvent event;
9822         event.xexpose.type = Expose;
9823         event.xexpose.serial = 0;
9824         event.xexpose.send_event = 1;
9825         event.xexpose.display = dpy;
9826         event.xexpose.window = _window;
9827         event.xexpose.x = 0;
9828         event.xexpose.y = 0;
9829         event.xexpose.width = width();
9830         event.xexpose.height = height();
9831         event.xexpose.count = 0;
9832         XSendEvent(dpy,_window,0,0,&event);
9833       } else { // Repaint directly (may be called from the expose event)
9834         GC gc = DefaultGC(dpy,DefaultScreen(dpy));
9835 
9836 #ifdef cimg_use_xshm
9837         if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
9838         else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
9839 #else
9840         XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
9841 #endif
9842       }
9843     }
9844 
9845     template<typename T>
9846     void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
9847       Display *const dpy = cimg::X11_attr().display;
9848       cimg::unused(pixel_type);
9849 
9850 #ifdef cimg_use_xshm
9851       if (_shminfo) {
9852         XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
9853         XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
9854                                                cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
9855         if (!nimage) { delete nshminfo; return; }
9856         else {
9857           nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
9858           if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
9859           else {
9860             nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
9861             if (nshminfo->shmaddr==(char*)-1) {
9862               shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
9863             } else {
9864               nshminfo->readOnly = 0;
9865               cimg::X11_attr().is_shm_enabled = true;
9866               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
9867               XShmAttach(dpy,nshminfo);
9868               XFlush(dpy);
9869               XSetErrorHandler(oldXErrorHandler);
9870               if (!cimg::X11_attr().is_shm_enabled) {
9871                 shmdt(nshminfo->shmaddr);
9872                 shmctl(nshminfo->shmid,IPC_RMID,0);
9873                 XDestroyImage(nimage);
9874                 delete nshminfo;
9875                 return;
9876               } else {
9877                 T *const ndata = (T*)nimage->data;
9878                 if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
9879                 else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
9880                 XShmDetach(dpy,_shminfo);
9881                 XDestroyImage(_image);
9882                 shmdt(_shminfo->shmaddr);
9883                 shmctl(_shminfo->shmid,IPC_RMID,0);
9884                 delete _shminfo;
9885                 _shminfo = nshminfo;
9886                 _image = nimage;
9887                 _data = (void*)ndata;
9888               }
9889             }
9890           }
9891         }
9892       } else
9893 #endif
9894         {
9895           T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
9896           if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
9897           else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
9898           _data = (void*)ndata;
9899           XDestroyImage(_image);
9900           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
9901                                 cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
9902         }
9903     }
9904 
9905     void _init_fullscreen() {
9906       if (!_is_fullscreen || _is_closed) return;
9907       Display *const dpy = cimg::X11_attr().display;
9908       _background_window = 0;
9909 
9910 #ifdef cimg_use_xrandr
9911       int foo;
9912       if (XRRQueryExtension(dpy,&foo,&foo)) {
9913         XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
9914         if (!cimg::X11_attr().resolutions) {
9915           cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
9916           cimg::X11_attr().nb_resolutions = (unsigned int)foo;
9917         }
9918         if (cimg::X11_attr().resolutions) {
9919           cimg::X11_attr().curr_resolution = 0;
9920           for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
9921             const unsigned int
9922               nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
9923               nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
9924             if (nw>=_width && nh>=_height &&
9925                 nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
9926                 nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
9927               cimg::X11_attr().curr_resolution = i;
9928           }
9929           if (cimg::X11_attr().curr_resolution>0) {
9930             XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
9931             XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
9932                                cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
9933             XRRFreeScreenConfigInfo(config);
9934             XSync(dpy,0);
9935           }
9936         }
9937       }
9938       if (!cimg::X11_attr().resolutions)
9939         cimg::warn(_cimgdisplay_instance
9940                    "init_fullscreen(): Xrandr extension not supported by the X server.",
9941                    cimgdisplay_instance);
9942 #endif
9943 
9944       const unsigned int sx = screen_width(), sy = screen_height();
9945       if (sx==_width && sy==_height) return;
9946       XSetWindowAttributes attr_set;
9947 
9948       attr_set.background_pixel = XBlackPixel(dpy,XDefaultScreen(dpy));
9949       attr_set.override_redirect = 1;
9950       _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
9951                                          InputOutput,CopyFromParent,CWBackPixel | CWOverrideRedirect,&attr_set);
9952       XEvent event;
9953       XSelectInput(dpy,_background_window,StructureNotifyMask);
9954       XMapRaised(dpy,_background_window);
9955       do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
9956       while (event.type!=MapNotify);
9957 
9958       XWindowAttributes attr;
9959       do {
9960         XGetWindowAttributes(dpy,_background_window,&attr);
9961         if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
9962       } while (attr.map_state!=IsViewable);
9963     }
9964 
9965     void _desinit_fullscreen() {
9966       if (!_is_fullscreen) return;
9967       Display *const dpy = cimg::X11_attr().display;
9968       XUngrabKeyboard(dpy,CurrentTime);
9969 
9970 #ifdef cimg_use_xrandr
9971       if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
9972         XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
9973         XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
9974         XRRFreeScreenConfigInfo(config);
9975         XSync(dpy,0);
9976         cimg::X11_attr().curr_resolution = 0;
9977       }
9978 #endif
9979       if (_background_window) XDestroyWindow(dpy,_background_window);
9980       _background_window = 0;
9981       _is_fullscreen = false;
9982     }
9983 
9984     static int _assign_xshm(Display *dpy, XErrorEvent *error) {
9985       cimg::unused(dpy,error);
9986       cimg::X11_attr().is_shm_enabled = false;
9987       return 0;
9988     }
9989 
9990     void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
9991                  const unsigned int normalization_type=3,
9992                  const bool fullscreen_flag=false, const bool closed_flag=false) {
9993       cimg::mutex(14);
9994 
9995       // Allocate space for window title
9996       const char *const nptitle = ptitle?ptitle:"";
9997       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
9998       char *const tmp_title = s?new char[s]:0;
9999       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
10000 
10001       // Destroy previous display window if existing
10002       if (!is_empty()) assign();
10003 
10004       // Open X11 display and retrieve graphical properties.
10005       Display* &dpy = cimg::X11_attr().display;
10006       if (!dpy) {
10007         dpy = XOpenDisplay(0);
10008         if (!dpy)
10009           throw CImgDisplayException(_cimgdisplay_instance
10010                                      "assign(): Failed to open X11 display.",
10011                                      cimgdisplay_instance);
10012 
10013         cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
10014         if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
10015             cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
10016           throw CImgDisplayException(_cimgdisplay_instance
10017                                      "assign(): Invalid %u bits screen mode detected "
10018                                      "(only 8, 16, 24 and 32 bits modes are managed).",
10019                                      cimgdisplay_instance,
10020                                      cimg::X11_attr().nb_bits);
10021         XVisualInfo vtemplate;
10022         vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
10023         int nb_visuals;
10024         XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
10025         if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
10026         cimg::X11_attr().byte_order = ImageByteOrder(dpy);
10027         XFree(vinfo);
10028 
10029         cimg_lock_display();
10030         cimg::X11_attr().events_thread = new pthread_t;
10031         pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
10032       } else cimg_lock_display();
10033 
10034       // Set display variables.
10035       _width = std::min(dimw,(unsigned int)screen_width());
10036       _height = std::min(dimh,(unsigned int)screen_height());
10037       _normalization = normalization_type<4?normalization_type:3;
10038       _is_fullscreen = fullscreen_flag;
10039       _window_x = _window_y = cimg::type<int>::min();
10040       _is_closed = closed_flag;
10041       _title = tmp_title;
10042       flush();
10043 
10044       // Create X11 window (and LUT, if 8bits display)
10045       if (_is_fullscreen) {
10046         if (!_is_closed) _init_fullscreen();
10047         const unsigned int sx = screen_width(), sy = screen_height();
10048         XSetWindowAttributes attr_set;
10049         attr_set.override_redirect = 1;
10050         _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0,
10051                                 InputOutput,CopyFromParent,CWOverrideRedirect,&attr_set);
10052       } else
10053         _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
10054 
10055       XSelectInput(dpy,_window,
10056                    ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
10057                    EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
10058 
10059       XStoreName(dpy,_window,_title?_title:" ");
10060       if (cimg::X11_attr().nb_bits==8) {
10061         _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
10062         _set_colormap(_colormap,3);
10063         XSetWindowColormap(dpy,_window,_colormap);
10064       }
10065 
10066       static const char *const _window_class = cimg_appname;
10067       XClassHint *const window_class = XAllocClassHint();
10068       window_class->res_name = (char*)_window_class;
10069       window_class->res_class = (char*)_window_class;
10070       XSetClassHint(dpy,_window,window_class);
10071       XFree(window_class);
10072 
10073       _window_width = _width;
10074       _window_height = _height;
10075 
10076       // Create XImage
10077 #ifdef cimg_use_xshm
10078       _shminfo = 0;
10079       if (XShmQueryExtension(dpy)) {
10080         _shminfo = new XShmSegmentInfo;
10081         _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
10082                                  ZPixmap,0,_shminfo,_width,_height);
10083         if (!_image) { delete _shminfo; _shminfo = 0; }
10084         else {
10085           _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
10086           if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
10087           else {
10088             _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
10089             if (_shminfo->shmaddr==(char*)-1) {
10090               shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
10091             } else {
10092               _shminfo->readOnly = 0;
10093               cimg::X11_attr().is_shm_enabled = true;
10094               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
10095               XShmAttach(dpy,_shminfo);
10096               XSync(dpy,0);
10097               XSetErrorHandler(oldXErrorHandler);
10098               if (!cimg::X11_attr().is_shm_enabled) {
10099                 shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
10100                 delete _shminfo; _shminfo = 0;
10101               }
10102             }
10103           }
10104         }
10105       }
10106       if (!_shminfo)
10107 #endif
10108         {
10109           const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1:
10110                                                                   (cimg::X11_attr().nb_bits==16?2:4));
10111           _data = std::malloc(buf_size);
10112           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
10113                                 ZPixmap,0,(char*)_data,_width,_height,8,0);
10114         }
10115 
10116       _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
10117       _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
10118       XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
10119 
10120       if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
10121       cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
10122       if (!_is_closed) _map_window(); else _window_x = _window_y = cimg::type<int>::min();
10123       cimg_unlock_display();
10124       cimg::mutex(14,0);
10125     }
10126 
10127     CImgDisplay& assign() {
10128       if (is_empty()) return flush();
10129       Display *const dpy = cimg::X11_attr().display;
10130       cimg_lock_display();
10131 
10132       // Remove display window from event thread list.
10133       unsigned int i;
10134       for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
10135       for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1];
10136       --cimg::X11_attr().nb_wins;
10137 
10138       // Destroy window, image, colormap and title.
10139       if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
10140 
10141 
10142 #ifdef cimg_use_xshm
10143       if (_shminfo) {
10144         XShmDetach(dpy,_shminfo);
10145         shmdt(_shminfo->shmaddr);
10146         shmctl(_shminfo->shmid,IPC_RMID,0);
10147         delete _shminfo;
10148         _shminfo = 0;
10149       }
10150 #endif
10151 
10152       XDestroyImage(_image);
10153       if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
10154       XDestroyWindow(dpy,_window);
10155       XSync(dpy,0);
10156       _window = 0; _colormap = 0; _data = 0; _image = 0;
10157 
10158       // Reset display variables.
10159       delete[] _title;
10160       _width = _height = _normalization = _window_width = _window_height = 0;
10161       _window_x = _window_y = cimg::type<int>::min();
10162       _is_fullscreen = false;
10163       _is_closed = true;
10164       _min = _max = 0;
10165       _title = 0;
10166       flush();
10167 
10168       cimg_unlock_display();
10169       return *this;
10170     }
10171 
10172     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
10173                         const unsigned int normalization_type=3,
10174                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10175       if (!dimw || !dimh) return assign();
10176       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
10177       _min = _max = 0;
10178       std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
10179                            (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*
10180                   (size_t)_width*_height);
10181       return paint();
10182     }
10183 
10184     template<typename T>
10185     CImgDisplay& assign(const CImg<T>& img, 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 (!img) return assign();
10189       CImg<T> tmp;
10190       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
10191                                                                            (img._height - 1)/2,
10192                                                                            (img._depth - 1)/2));
10193       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
10194       if (_normalization==2) _min = (float)nimg.min_max(_max);
10195       return render(nimg).paint();
10196     }
10197 
10198     template<typename T>
10199     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
10200                         const unsigned int normalization_type=3,
10201                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10202       if (!list) return assign();
10203       CImg<T> tmp;
10204       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
10205                                                                                            (img._height - 1)/2,
10206                                                                                            (img._depth - 1)/2));
10207       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
10208       if (_normalization==2) _min = (float)nimg.min_max(_max);
10209       return render(nimg).paint();
10210     }
10211 
10212     CImgDisplay& assign(const CImgDisplay& disp) {
10213       if (!disp) return assign();
10214       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
10215       std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
10216                                     cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
10217                                     sizeof(unsigned int))*(size_t)_width*_height);
10218       return paint();
10219     }
10220 
10221     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
10222       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
10223       if (is_empty()) return assign(nwidth,nheight);
10224       Display *const dpy = cimg::X11_attr().display;
10225       const unsigned int
10226         tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
10227         tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
10228         dimx = tmpdimx?tmpdimx:1,
10229         dimy = tmpdimy?tmpdimy:1;
10230       if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
10231         show();
10232         cimg_lock_display();
10233         if (_window_width!=dimx || _window_height!=dimy) {
10234           XWindowAttributes attr;
10235           for (unsigned int i = 0; i<10; ++i) {
10236             XResizeWindow(dpy,_window,dimx,dimy);
10237             XGetWindowAttributes(dpy,_window,&attr);
10238             if (attr.width==(int)dimx && attr.height==(int)dimy) break;
10239             cimg::wait(5,&_timer);
10240           }
10241         }
10242         if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
10243           case 8 :  { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
10244           case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
10245           default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
10246           }
10247         _window_width = _width = dimx; _window_height = _height = dimy;
10248         cimg_unlock_display();
10249       }
10250       _is_resized = false;
10251       if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2);
10252       if (force_redraw) return paint();
10253       return *this;
10254     }
10255 
10256     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
10257       if (is_empty()) return *this;
10258       if (force_redraw) {
10259         const cimg_ulong buf_size = (cimg_ulong)_width*_height*
10260           (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
10261         void *image_data = std::malloc(buf_size);
10262         std::memcpy(image_data,_data,buf_size);
10263         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
10264         std::memcpy(_data,image_data,buf_size);
10265         std::free(image_data);
10266         return paint();
10267       }
10268       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
10269     }
10270 
10271     CImgDisplay& show() {
10272       if (is_empty() || !_is_closed) return *this;
10273       cimg_lock_display();
10274       _is_closed = false;
10275       if (_is_fullscreen) _init_fullscreen();
10276       _map_window();
10277       cimg_unlock_display();
10278       return paint();
10279     }
10280 
10281     CImgDisplay& close() {
10282       if (is_empty() || _is_closed) return *this;
10283       Display *const dpy = cimg::X11_attr().display;
10284       cimg_lock_display();
10285       if (_is_fullscreen) _desinit_fullscreen();
10286       XUnmapWindow(dpy,_window);
10287       _window_x = _window_y = cimg::type<int>::min();
10288       _is_closed = true;
10289       cimg_unlock_display();
10290       return *this;
10291     }
10292 
10293     CImgDisplay& move(const int posx, const int posy) {
10294       if (is_empty()) return *this;
10295       show();
10296       if (_window_x!=posx || _window_y!=posy) {
10297         Display *const dpy = cimg::X11_attr().display;
10298         cimg_lock_display();
10299         XMoveWindow(dpy,_window,posx,posy);
10300         _window_x = posx;
10301         _window_y = posy;
10302         cimg_unlock_display();
10303       }
10304       _is_moved = false;
10305       return paint();
10306     }
10307 
10308     CImgDisplay& show_mouse() {
10309       if (is_empty()) return *this;
10310       Display *const dpy = cimg::X11_attr().display;
10311       cimg_lock_display();
10312       XUndefineCursor(dpy,_window);
10313       cimg_unlock_display();
10314       return *this;
10315     }
10316 
10317     CImgDisplay& hide_mouse() {
10318       if (is_empty()) return *this;
10319       Display *const dpy = cimg::X11_attr().display;
10320       cimg_lock_display();
10321       static const char pix_data[8] = { 0 };
10322       XColor col;
10323       col.red = col.green = col.blue = 0;
10324       Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
10325       Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
10326       XFreePixmap(dpy,pix);
10327       XDefineCursor(dpy,_window,cur);
10328       cimg_unlock_display();
10329       return *this;
10330     }
10331 
10332     CImgDisplay& set_mouse(const int posx, const int posy) {
10333       if (is_empty() || _is_closed) return *this;
10334       Display *const dpy = cimg::X11_attr().display;
10335       cimg_lock_display();
10336       XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
10337       _mouse_x = posx; _mouse_y = posy;
10338       _is_moved = false;
10339       XSync(dpy,0);
10340       cimg_unlock_display();
10341       return *this;
10342     }
10343 
10344     CImgDisplay& set_title(const char *const format, ...) {
10345       if (is_empty()) return *this;
10346       char *const tmp = new char[1024];
10347       va_list ap;
10348       va_start(ap, format);
10349       cimg_vsnprintf(tmp,1024,format,ap);
10350       va_end(ap);
10351       if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
10352       delete[] _title;
10353       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
10354       _title = new char[s];
10355       std::memcpy(_title,tmp,s*sizeof(char));
10356       Display *const dpy = cimg::X11_attr().display;
10357       cimg_lock_display();
10358       XStoreName(dpy,_window,tmp);
10359       cimg_unlock_display();
10360       delete[] tmp;
10361       return *this;
10362     }
10363 
10364     template<typename T>
10365     CImgDisplay& display(const CImg<T>& img) {
10366       if (!img)
10367         throw CImgArgumentException(_cimgdisplay_instance
10368                                     "display(): Empty specified image.",
10369                                     cimgdisplay_instance);
10370       if (is_empty()) return assign(img);
10371       return render(img).paint(false);
10372     }
10373 
10374     CImgDisplay& paint(const bool wait_expose=true) {
10375       if (is_empty()) return *this;
10376       cimg_lock_display();
10377       _paint(wait_expose);
10378       cimg_unlock_display();
10379       return *this;
10380     }
10381 
10382     template<typename T>
10383     CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
10384       if (!img)
10385         throw CImgArgumentException(_cimgdisplay_instance
10386                                     "render(): Empty specified image.",
10387                                     cimgdisplay_instance);
10388       if (is_empty()) return *this;
10389       if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
10390                                                              (img._depth - 1)/2));
10391       if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height))
10392         return render(img.get_resize(_width,_height,1,-100,1));
10393       if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
10394         static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
10395         return render(img.get_index(default_colormap,1,false));
10396       }
10397 
10398       const T
10399         *data1 = img._data,
10400         *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
10401         *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
10402 
10403       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
10404       cimg_lock_display();
10405 
10406       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
10407         _min = _max = 0;
10408         switch (cimg::X11_attr().nb_bits) {
10409         case 8 : { // 256 colormap, no normalization
10410           _set_colormap(_colormap,img._spectrum);
10411           unsigned char
10412             *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
10413             new unsigned char[(size_t)img._width*img._height],
10414             *ptrd = (unsigned char*)ndata;
10415           switch (img._spectrum) {
10416           case 1 :
10417             for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10418               (*ptrd++) = (unsigned char)*(data1++);
10419             break;
10420           case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10421               const unsigned char
10422                 R = (unsigned char)*(data1++),
10423                 G = (unsigned char)*(data2++);
10424               (*ptrd++) = (R&0xf0) | (G>>4);
10425             } break;
10426           default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10427               const unsigned char
10428                 R = (unsigned char)*(data1++),
10429                 G = (unsigned char)*(data2++),
10430                 B = (unsigned char)*(data3++);
10431               (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
10432             }
10433           }
10434           if (ndata!=_data) {
10435             _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
10436             delete[] ndata;
10437           }
10438         } break;
10439         case 16 : { // 16 bits colors, no normalization
10440           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
10441             new unsigned short[(size_t)img._width*img._height];
10442           unsigned char *ptrd = (unsigned char*)ndata;
10443           const unsigned int M = 248;
10444           switch (img._spectrum) {
10445           case 1 :
10446             if (cimg::X11_attr().byte_order)
10447               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10448                 const unsigned char val = (unsigned char)*(data1++), G = val>>2;
10449                 ptrd[0] = (val&M) | (G>>3);
10450                 ptrd[1] = (G<<5) | (G>>1);
10451                 ptrd+=2;
10452               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10453                 const unsigned char val = (unsigned char)*(data1++), G = val>>2;
10454                 ptrd[0] = (G<<5) | (G>>1);
10455                 ptrd[1] = (val&M) | (G>>3);
10456                 ptrd+=2;
10457               }
10458             break;
10459           case 2 :
10460             if (cimg::X11_attr().byte_order)
10461               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10462                 const unsigned char G = (unsigned char)*(data2++)>>2;
10463                 ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
10464                 ptrd[1] = (G<<5);
10465                 ptrd+=2;
10466               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10467                 const unsigned char G = (unsigned char)*(data2++)>>2;
10468                 ptrd[0] = (G<<5);
10469                 ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
10470                 ptrd+=2;
10471               }
10472             break;
10473           default :
10474             if (cimg::X11_attr().byte_order)
10475               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10476                 const unsigned char G = (unsigned char)*(data2++)>>2;
10477                 ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
10478                 ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3);
10479                 ptrd+=2;
10480               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10481                 const unsigned char G = (unsigned char)*(data2++)>>2;
10482                 ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3);
10483                 ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
10484                 ptrd+=2;
10485               }
10486           }
10487           if (ndata!=_data) {
10488             _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
10489             delete[] ndata;
10490           }
10491         } break;
10492         default : { // 24 bits colors, no normalization
10493           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
10494             new unsigned int[(size_t)img._width*img._height];
10495           if (sizeof(int)==4) { // 32 bits int uses optimized version
10496             unsigned int *ptrd = ndata;
10497             switch (img._spectrum) {
10498             case 1 :
10499               if (cimg::X11_attr().byte_order==cimg::endianness())
10500                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10501                   const unsigned char val = (unsigned char)*(data1++);
10502                   *(ptrd++) = (val<<16) | (val<<8) | val;
10503                 }
10504               else
10505                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10506                  const unsigned char val = (unsigned char)*(data1++);
10507                   *(ptrd++) = (val<<16) | (val<<8) | val;
10508                 }
10509               break;
10510             case 2 :
10511               if (cimg::X11_attr().byte_order==cimg::endianness())
10512                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10513                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
10514               else
10515                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10516                   *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
10517               break;
10518             default :
10519               if (cimg::X11_attr().byte_order==cimg::endianness())
10520                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10521                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) |
10522                     (unsigned char)*(data3++);
10523               else
10524                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10525                   *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) |
10526                     ((unsigned char)*(data1++)<<8);
10527             }
10528           } else {
10529             unsigned char *ptrd = (unsigned char*)ndata;
10530             switch (img._spectrum) {
10531             case 1 :
10532               if (cimg::X11_attr().byte_order)
10533                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10534                   ptrd[0] = 0;
10535                   ptrd[1] = (unsigned char)*(data1++);
10536                   ptrd[2] = 0;
10537                   ptrd[3] = 0;
10538                   ptrd+=4;
10539                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10540                   ptrd[0] = 0;
10541                   ptrd[1] = 0;
10542                   ptrd[2] = (unsigned char)*(data1++);
10543                   ptrd[3] = 0;
10544                   ptrd+=4;
10545                 }
10546               break;
10547             case 2 :
10548               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
10549               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10550                 ptrd[0] = 0;
10551                 ptrd[1] = (unsigned char)*(data2++);
10552                 ptrd[2] = (unsigned char)*(data1++);
10553                 ptrd[3] = 0;
10554                 ptrd+=4;
10555               }
10556               break;
10557             default :
10558               if (cimg::X11_attr().byte_order)
10559                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10560                   ptrd[0] = 0;
10561                   ptrd[1] = (unsigned char)*(data1++);
10562                   ptrd[2] = (unsigned char)*(data2++);
10563                   ptrd[3] = (unsigned char)*(data3++);
10564                   ptrd+=4;
10565                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10566                   ptrd[0] = (unsigned char)*(data3++);
10567                   ptrd[1] = (unsigned char)*(data2++);
10568                   ptrd[2] = (unsigned char)*(data1++);
10569                   ptrd[3] = 0;
10570                   ptrd+=4;
10571                 }
10572             }
10573           }
10574           if (ndata!=_data) {
10575             _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
10576             delete[] ndata;
10577           }
10578         }
10579         }
10580       } else {
10581         if (_normalization==3) {
10582           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
10583           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
10584         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
10585         const float delta = _max - _min, mm = 255/(delta?delta:1.f);
10586         switch (cimg::X11_attr().nb_bits) {
10587         case 8 : { // 256 colormap, with normalization
10588           _set_colormap(_colormap,img._spectrum);
10589           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
10590             new unsigned char[(size_t)img._width*img._height];
10591           unsigned char *ptrd = (unsigned char*)ndata;
10592           switch (img._spectrum) {
10593           case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10594               const unsigned char R = (unsigned char)((*(data1++) - _min)*mm);
10595               *(ptrd++) = R;
10596             } break;
10597           case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10598               const unsigned char
10599                 R = (unsigned char)((*(data1++) - _min)*mm),
10600                 G = (unsigned char)((*(data2++) - _min)*mm);
10601             (*ptrd++) = (R&0xf0) | (G>>4);
10602           } break;
10603           default :
10604             for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10605               const unsigned char
10606                 R = (unsigned char)((*(data1++) - _min)*mm),
10607                 G = (unsigned char)((*(data2++) - _min)*mm),
10608                 B = (unsigned char)((*(data3++) - _min)*mm);
10609               *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
10610             }
10611           }
10612           if (ndata!=_data) {
10613             _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
10614             delete[] ndata;
10615           }
10616         } break;
10617         case 16 : { // 16 bits colors, with normalization
10618           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
10619             new unsigned short[(size_t)img._width*img._height];
10620           unsigned char *ptrd = (unsigned char*)ndata;
10621           const unsigned int M = 248;
10622           switch (img._spectrum) {
10623           case 1 :
10624             if (cimg::X11_attr().byte_order)
10625               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10626                 const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
10627                 ptrd[0] = (val&M) | (G>>3);
10628                 ptrd[1] = (G<<5) | (val>>3);
10629                 ptrd+=2;
10630               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10631                 const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
10632                 ptrd[0] = (G<<5) | (val>>3);
10633                 ptrd[1] = (val&M) | (G>>3);
10634                 ptrd+=2;
10635               }
10636             break;
10637           case 2 :
10638             if (cimg::X11_attr().byte_order)
10639               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10640                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
10641                 ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
10642                 ptrd[1] = (G<<5);
10643                 ptrd+=2;
10644               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10645                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
10646                 ptrd[0] = (G<<5);
10647                 ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
10648                 ptrd+=2;
10649               }
10650             break;
10651           default :
10652             if (cimg::X11_attr().byte_order)
10653               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10654                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
10655                 ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
10656                 ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
10657                 ptrd+=2;
10658               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10659                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
10660                 ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
10661                 ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
10662                 ptrd+=2;
10663               }
10664           }
10665           if (ndata!=_data) {
10666             _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
10667             delete[] ndata;
10668           }
10669         } break;
10670         default : { // 24 bits colors, with normalization
10671           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
10672             new unsigned int[(size_t)img._width*img._height];
10673           if (sizeof(int)==4) { // 32 bits int uses optimized version
10674             unsigned int *ptrd = ndata;
10675             switch (img._spectrum) {
10676             case 1 :
10677               if (cimg::X11_attr().byte_order==cimg::endianness())
10678                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10679                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10680                   *(ptrd++) = (val<<16) | (val<<8) | val;
10681                 }
10682               else
10683                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10684                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10685                   *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
10686                 }
10687               break;
10688             case 2 :
10689               if (cimg::X11_attr().byte_order==cimg::endianness())
10690                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10691                   *(ptrd++) =
10692                     ((unsigned char)((*(data1++) - _min)*mm)<<16) |
10693                     ((unsigned char)((*(data2++) - _min)*mm)<<8);
10694               else
10695                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10696                   *(ptrd++) =
10697                     ((unsigned char)((*(data2++) - _min)*mm)<<16) |
10698                     ((unsigned char)((*(data1++) - _min)*mm)<<8);
10699               break;
10700             default :
10701               if (cimg::X11_attr().byte_order==cimg::endianness())
10702                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10703                   *(ptrd++) =
10704                     ((unsigned char)((*(data1++) - _min)*mm)<<16) |
10705                     ((unsigned char)((*(data2++) - _min)*mm)<<8) |
10706                     (unsigned char)((*(data3++) - _min)*mm);
10707               else
10708                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
10709                   *(ptrd++) =
10710                     ((unsigned char)((*(data3++) - _min)*mm)<<24) |
10711                     ((unsigned char)((*(data2++) - _min)*mm)<<16) |
10712                     ((unsigned char)((*(data1++) - _min)*mm)<<8);
10713             }
10714           } else {
10715             unsigned char *ptrd = (unsigned char*)ndata;
10716             switch (img._spectrum) {
10717             case 1 :
10718               if (cimg::X11_attr().byte_order)
10719                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10720                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10721                   ptrd[0] = 0;
10722                   ptrd[1] = val;
10723                   ptrd[2] = val;
10724                   ptrd[3] = val;
10725                   ptrd+=4;
10726                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10727                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10728                   ptrd[0] = val;
10729                   ptrd[1] = val;
10730                   ptrd[2] = val;
10731                   ptrd[3] = 0;
10732                   ptrd+=4;
10733                 }
10734               break;
10735             case 2 :
10736               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
10737               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10738                 ptrd[0] = 0;
10739                 ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
10740                 ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
10741                 ptrd[3] = 0;
10742                 ptrd+=4;
10743               }
10744               break;
10745             default :
10746               if (cimg::X11_attr().byte_order)
10747                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10748                   ptrd[0] = 0;
10749                   ptrd[1] = (unsigned char)((*(data1++) - _min)*mm);
10750                   ptrd[2] = (unsigned char)((*(data2++) - _min)*mm);
10751                   ptrd[3] = (unsigned char)((*(data3++) - _min)*mm);
10752                   ptrd+=4;
10753                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10754                   ptrd[0] = (unsigned char)((*(data3++) - _min)*mm);
10755                   ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
10756                   ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
10757                   ptrd[3] = 0;
10758                   ptrd+=4;
10759                 }
10760             }
10761           }
10762           if (ndata!=_data) {
10763             _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
10764             delete[] ndata;
10765           }
10766         }
10767         }
10768       }
10769       cimg_unlock_display();
10770       return *this;
10771     }
10772 
10773     template<typename T>
10774     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
10775       img.assign();
10776       Display *dpy = cimg::X11_attr().display;
10777       cimg_lock_display();
10778       if (!dpy) {
10779         dpy = XOpenDisplay(0);
10780         if (!dpy)
10781           throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display.");
10782       }
10783       Window root = DefaultRootWindow(dpy);
10784       XWindowAttributes gwa;
10785       XGetWindowAttributes(dpy,root,&gwa);
10786       const int width = gwa.width, height = gwa.height;
10787       int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
10788       if (_x0>_x1) cimg::swap(_x0,_x1);
10789       if (_y0>_y1) cimg::swap(_y0,_y1);
10790 
10791       XImage *image = 0;
10792       if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
10793         _x0 = std::max(_x0,0);
10794         _y0 = std::max(_y0,0);
10795         _x1 = std::min(_x1,width - 1);
10796         _y1 = std::min(_y1,height - 1);
10797         image = XGetImage(dpy,root,_x0,_y0,_x1 - _x0 + 1,_y1 - _y0 + 1,AllPlanes,ZPixmap);
10798 
10799         if (image) {
10800           const unsigned long
10801             red_mask = image->red_mask,
10802             green_mask = image->green_mask,
10803             blue_mask = image->blue_mask;
10804           img.assign(image->width,image->height,1,3);
10805           T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
10806           cimg_forXY(img,x,y) {
10807             const unsigned long pixel = XGetPixel(image,x,y);
10808             *(pR++) = (T)((pixel & red_mask)>>16);
10809             *(pG++) = (T)((pixel & green_mask)>>8);
10810             *(pB++) = (T)(pixel & blue_mask);
10811           }
10812           XDestroyImage(image);
10813         }
10814       }
10815       if (!cimg::X11_attr().display) XCloseDisplay(dpy);
10816       cimg_unlock_display();
10817       if (img.is_empty())
10818         throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
10819                                    "with coordinates (%d,%d)-(%d,%d).",
10820                                    x0,y0,x1,y1);
10821     }
10822 
10823     template<typename T>
10824     const CImgDisplay& snapshot(CImg<T>& img) const {
10825       if (is_empty()) { img.assign(); return *this; }
10826       const unsigned char *ptrs = (unsigned char*)_data;
10827       img.assign(_width,_height,1,3);
10828       T
10829         *data1 = img.data(0,0,0,0),
10830         *data2 = img.data(0,0,0,1),
10831         *data3 = img.data(0,0,0,2);
10832       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
10833       switch (cimg::X11_attr().nb_bits) {
10834       case 8 : {
10835         for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10836           const unsigned char val = *(ptrs++);
10837           *(data1++) = (T)(val&0xe0);
10838           *(data2++) = (T)((val&0x1c)<<3);
10839           *(data3++) = (T)(val<<6);
10840         }
10841       } break;
10842       case 16 : {
10843         if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10844           const unsigned char
10845             val0 = ptrs[0],
10846             val1 = ptrs[1];
10847           ptrs+=2;
10848           *(data1++) = (T)(val0&0xf8);
10849           *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
10850           *(data3++) = (T)(val1<<3);
10851           } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10852           const unsigned short
10853             val0 = ptrs[0],
10854             val1 = ptrs[1];
10855           ptrs+=2;
10856           *(data1++) = (T)(val1&0xf8);
10857           *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
10858           *(data3++) = (T)(val0<<3);
10859         }
10860       } break;
10861       default : {
10862         if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10863           ++ptrs;
10864           *(data1++) = (T)ptrs[0];
10865           *(data2++) = (T)ptrs[1];
10866           *(data3++) = (T)ptrs[2];
10867           ptrs+=3;
10868           } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10869             *(data3++) = (T)ptrs[0];
10870             *(data2++) = (T)ptrs[1];
10871             *(data1++) = (T)ptrs[2];
10872             ptrs+=3;
10873             ++ptrs;
10874           }
10875       }
10876       }
10877       return *this;
10878     }
10879 
10880     // Windows-based implementation.
10881     //-------------------------------
10882 #elif cimg_display==2
10883 
10884     bool _is_mouse_tracked, _is_cursor_visible;
10885     HANDLE _thread, _is_created, _mutex;
10886     HWND _window, _background_window;
10887     CLIENTCREATESTRUCT _ccs;
10888     unsigned int *_data;
10889     DEVMODE _curr_mode;
10890     BITMAPINFO _bmi;
10891     HDC _hdc;
10892 
10893     static int screen_width() {
10894       DEVMODE mode;
10895       mode.dmSize = sizeof(DEVMODE);
10896       mode.dmDriverExtra = 0;
10897       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
10898       return (int)mode.dmPelsWidth;
10899     }
10900 
10901     static int screen_height() {
10902       DEVMODE mode;
10903       mode.dmSize = sizeof(DEVMODE);
10904       mode.dmDriverExtra = 0;
10905       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
10906       return (int)mode.dmPelsHeight;
10907     }
10908 
10909     static void wait_all() {
10910       WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
10911     }
10912 
10913     static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
10914 #ifdef _WIN64
10915       CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
10916 #else
10917       CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
10918 #endif
10919       MSG st_msg;
10920       switch (msg) {
10921       case WM_CLOSE :
10922         disp->_mouse_x = disp->_mouse_y = -1;
10923         disp->_window_x = disp->_window_y = cimg::type<int>::min();
10924         disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
10925         ReleaseMutex(disp->_mutex);
10926         ShowWindow(disp->_window,SW_HIDE);
10927         disp->_is_event = true;
10928         SetEvent(cimg::Win32_attr().wait_event);
10929         return 0;
10930       case WM_SIZE : {
10931         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
10932         WaitForSingleObject(disp->_mutex,INFINITE);
10933         const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
10934         if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
10935           disp->_window_width = nw;
10936           disp->_window_height = nh;
10937           disp->_mouse_x = disp->_mouse_y = -1;
10938           disp->_is_resized = disp->_is_event = true;
10939           SetEvent(cimg::Win32_attr().wait_event);
10940         }
10941         ReleaseMutex(disp->_mutex);
10942       } break;
10943       case WM_MOVE : {
10944         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
10945         WaitForSingleObject(disp->_mutex,INFINITE);
10946         const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
10947         if (nx!=disp->_window_x || ny!=disp->_window_y) {
10948           disp->_window_x = nx;
10949           disp->_window_y = ny;
10950           disp->_is_moved = disp->_is_event = true;
10951           SetEvent(cimg::Win32_attr().wait_event);
10952         }
10953         ReleaseMutex(disp->_mutex);
10954       } break;
10955       case WM_PAINT :
10956         disp->paint();
10957         cimg_lock_display();
10958         if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0);
10959         cimg_unlock_display();
10960         break;
10961       case WM_ERASEBKGND :
10962         //        return 0;
10963         break;
10964       case WM_KEYDOWN :
10965         disp->set_key((unsigned int)wParam);
10966         SetEvent(cimg::Win32_attr().wait_event);
10967         break;
10968       case WM_KEYUP :
10969         disp->set_key((unsigned int)wParam,false);
10970         SetEvent(cimg::Win32_attr().wait_event);
10971         break;
10972       case WM_MOUSEMOVE : {
10973         while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
10974         disp->_mouse_x = LOWORD(lParam);
10975         disp->_mouse_y = HIWORD(lParam);
10976 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
10977         if (!disp->_is_mouse_tracked) {
10978           TRACKMOUSEEVENT tme;
10979           tme.cbSize = sizeof(TRACKMOUSEEVENT);
10980           tme.dwFlags = TME_LEAVE;
10981           tme.hwndTrack = disp->_window;
10982           if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
10983         }
10984 #endif
10985         if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
10986           disp->_mouse_x = disp->_mouse_y = -1;
10987         disp->_is_event = true;
10988         SetEvent(cimg::Win32_attr().wait_event);
10989         cimg_lock_display();
10990         if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0);
10991         cimg_unlock_display();
10992       } break;
10993       case WM_MOUSELEAVE : {
10994         disp->_mouse_x = disp->_mouse_y = -1;
10995         disp->_is_mouse_tracked = false;
10996         cimg_lock_display();
10997         while (ShowCursor(TRUE)<0) {}
10998         cimg_unlock_display();
10999       } break;
11000       case WM_LBUTTONDOWN :
11001         disp->set_button(1);
11002         SetEvent(cimg::Win32_attr().wait_event);
11003         break;
11004       case WM_RBUTTONDOWN :
11005         disp->set_button(2);
11006         SetEvent(cimg::Win32_attr().wait_event);
11007         break;
11008       case WM_MBUTTONDOWN :
11009         disp->set_button(3);
11010         SetEvent(cimg::Win32_attr().wait_event);
11011         break;
11012       case WM_LBUTTONUP :
11013         disp->set_button(1,false);
11014         SetEvent(cimg::Win32_attr().wait_event);
11015         break;
11016       case WM_RBUTTONUP :
11017         disp->set_button(2,false);
11018         SetEvent(cimg::Win32_attr().wait_event);
11019         break;
11020       case WM_MBUTTONUP :
11021         disp->set_button(3,false);
11022         SetEvent(cimg::Win32_attr().wait_event);
11023         break;
11024       case 0x020A : // WM_MOUSEWHEEL:
11025         disp->set_wheel((int)((short)HIWORD(wParam))/120);
11026         SetEvent(cimg::Win32_attr().wait_event);
11027       }
11028       return DefWindowProc(window,msg,wParam,lParam);
11029     }
11030 
11031     static DWORD WINAPI _events_thread(void* arg) {
11032       CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
11033       const char *const title = (const char*)(((void**)arg)[1]);
11034       MSG msg;
11035       delete[] (void**)arg;
11036       disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
11037       disp->_bmi.bmiHeader.biWidth = disp->width();
11038       disp->_bmi.bmiHeader.biHeight = -disp->height();
11039       disp->_bmi.bmiHeader.biPlanes = 1;
11040       disp->_bmi.bmiHeader.biBitCount = 32;
11041       disp->_bmi.bmiHeader.biCompression = BI_RGB;
11042       disp->_bmi.bmiHeader.biSizeImage = 0;
11043       disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
11044       disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
11045       disp->_bmi.bmiHeader.biClrUsed = 0;
11046       disp->_bmi.bmiHeader.biClrImportant = 0;
11047       disp->_data = new unsigned int[(size_t)disp->_width*disp->_height];
11048       if (!disp->_is_fullscreen) { // Normal window
11049         RECT rect;
11050         rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1;
11051         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
11052         const int
11053           border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2),
11054           border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1),
11055           ww = disp->_width + 2*border1,
11056           wh = disp->_height + border1 + border2,
11057           sw = CImgDisplay::screen_width(),
11058           sh = CImgDisplay::screen_height();
11059         int
11060           wx = (int)cimg::round(cimg::rand(0,sw - ww -1)),
11061           wy = (int)cimg::round(cimg::rand(64,sh - wh - 65));
11062         if (wx + ww>=sw) wx = sw - ww;
11063         if (wy + wh>=sh) wy = sh - wh;
11064         if (wx<0) wx = 0;
11065         if (wy<0) wy = 0;
11066         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
11067                                       WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE),
11068                                       wx,wy,ww,wh,0,0,0,&(disp->_ccs));
11069         if (!disp->_is_closed) {
11070           GetWindowRect(disp->_window,&rect);
11071           disp->_window_x = rect.left;
11072           disp->_window_y = rect.top;
11073         } else disp->_window_x = disp->_window_y = cimg::type<int>::min();
11074       } else { // Fullscreen window
11075         const unsigned int
11076           sx = (unsigned int)screen_width(),
11077           sy = (unsigned int)screen_height();
11078         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
11079                                      WS_POPUP | (disp->_is_closed?0:WS_VISIBLE),
11080                                       (sx - disp->_width)/2,
11081                                       (sy - disp->_height)/2,
11082                                      disp->_width,disp->_height,0,0,0,&(disp->_ccs));
11083         disp->_window_x = disp->_window_y = 0;
11084       }
11085       SetForegroundWindow(disp->_window);
11086       disp->_hdc = GetDC(disp->_window);
11087       disp->_window_width = disp->_width;
11088       disp->_window_height = disp->_height;
11089       disp->flush();
11090 #ifdef _WIN64
11091       SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
11092       SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
11093 #else
11094       SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
11095       SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
11096 #endif
11097       SetEvent(disp->_is_created);
11098       while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
11099       return 0;
11100     }
11101 
11102     CImgDisplay& _update_window_pos() {
11103       if (_is_closed) _window_x = _window_y = cimg::type<int>::min();
11104       else {
11105         RECT rect;
11106         rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1;
11107         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
11108         GetWindowRect(_window,&rect);
11109         _window_x = rect.left;
11110         _window_y = rect.top;
11111       }
11112       return *this;
11113     }
11114 
11115     void _init_fullscreen() {
11116       _background_window = 0;
11117       if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
11118       else {
11119         DEVMODE mode;
11120         unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
11121         for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
11122           const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
11123           if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
11124             bestbpp = mode.dmBitsPerPel;
11125             ibest = imode;
11126             bw = nw; bh = nh;
11127           }
11128         }
11129         if (bestbpp) {
11130           _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
11131           EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
11132           EnumDisplaySettings(0,ibest,&mode);
11133           ChangeDisplaySettings(&mode,0);
11134         } else _curr_mode.dmSize = 0;
11135 
11136         const unsigned int
11137           sx = (unsigned int)screen_width(),
11138           sy = (unsigned int)screen_height();
11139         if (sx!=_width || sy!=_height) {
11140           CLIENTCREATESTRUCT background_ccs = { 0,0 };
11141           _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
11142           SetForegroundWindow(_background_window);
11143         }
11144       }
11145     }
11146 
11147     void _desinit_fullscreen() {
11148       if (!_is_fullscreen) return;
11149       if (_background_window) DestroyWindow(_background_window);
11150       _background_window = 0;
11151       if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
11152       _is_fullscreen = false;
11153     }
11154 
11155     CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
11156                          const unsigned int normalization_type=3,
11157                          const bool fullscreen_flag=false, const bool closed_flag=false) {
11158 
11159       // Allocate space for window title
11160       const char *const nptitle = ptitle?ptitle:"";
11161       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
11162       char *const tmp_title = s?new char[s]:0;
11163       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
11164 
11165       // Destroy previous window if existing
11166       if (!is_empty()) assign();
11167 
11168       // Set display variables
11169       _width = std::min(dimw,(unsigned int)screen_width());
11170       _height = std::min(dimh,(unsigned int)screen_height());
11171       _normalization = normalization_type<4?normalization_type:3;
11172       _is_fullscreen = fullscreen_flag;
11173       _window_x = _window_y = cimg::type<int>::min();
11174       _is_closed = closed_flag;
11175       _is_cursor_visible = true;
11176       _is_mouse_tracked = false;
11177       _title = tmp_title;
11178       flush();
11179       if (_is_fullscreen) _init_fullscreen();
11180 
11181       // Create event thread
11182       void *const arg = (void*)(new void*[2]);
11183       ((void**)arg)[0] = (void*)this;
11184       ((void**)arg)[1] = (void*)_title;
11185       _mutex = CreateMutex(0,FALSE_WIN,0);
11186       _is_created = CreateEvent(0,FALSE_WIN,FALSE_WIN,0);
11187       _thread = CreateThread(0,0,_events_thread,arg,0,0);
11188       WaitForSingleObject(_is_created,INFINITE);
11189       return *this;
11190     }
11191 
11192     CImgDisplay& assign() {
11193       if (is_empty()) return flush();
11194       DestroyWindow(_window);
11195       TerminateThread(_thread,0);
11196       delete[] _data;
11197       delete[] _title;
11198       _data = 0;
11199       _title = 0;
11200       if (_is_fullscreen) _desinit_fullscreen();
11201       _width = _height = _normalization = _window_width = _window_height = 0;
11202       _window_x = _window_y = cimg::type<int>::min();
11203       _is_fullscreen = false;
11204       _is_closed = true;
11205       _min = _max = 0;
11206       _title = 0;
11207       flush();
11208       return *this;
11209     }
11210 
11211     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
11212                         const unsigned int normalization_type=3,
11213                         const bool fullscreen_flag=false, const bool closed_flag=false) {
11214       if (!dimw || !dimh) return assign();
11215       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
11216       _min = _max = 0;
11217       std::memset(_data,0,sizeof(unsigned int)*_width*_height);
11218       return paint();
11219     }
11220 
11221     template<typename T>
11222     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
11223                         const unsigned int normalization_type=3,
11224                         const bool fullscreen_flag=false, const bool closed_flag=false) {
11225       if (!img) return assign();
11226       CImg<T> tmp;
11227       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
11228                                                                            (img._height - 1)/2,
11229                                                                            (img._depth - 1)/2));
11230       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
11231       if (_normalization==2) _min = (float)nimg.min_max(_max);
11232       return display(nimg);
11233     }
11234 
11235     template<typename T>
11236     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
11237                         const unsigned int normalization_type=3,
11238                         const bool fullscreen_flag=false, const bool closed_flag=false) {
11239       if (!list) return assign();
11240       CImg<T> tmp;
11241       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
11242                                                                                            (img._height - 1)/2,
11243                                                                                            (img._depth - 1)/2));
11244       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
11245       if (_normalization==2) _min = (float)nimg.min_max(_max);
11246       return display(nimg);
11247     }
11248 
11249     CImgDisplay& assign(const CImgDisplay& disp) {
11250       if (!disp) return assign();
11251       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
11252       std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
11253       return paint();
11254     }
11255 
11256     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
11257       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
11258       if (is_empty()) return assign(nwidth,nheight);
11259       const unsigned int
11260         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
11261         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
11262         dimx = tmpdimx?tmpdimx:1,
11263         dimy = tmpdimy?tmpdimy:1;
11264       if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
11265         if (_window_width!=dimx || _window_height!=dimy) {
11266           RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1;
11267           AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
11268           const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
11269           SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
11270         }
11271         if (_width!=dimx || _height!=dimy) {
11272           unsigned int *const ndata = new unsigned int[dimx*dimy];
11273           if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
11274           else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
11275           delete[] _data;
11276           _data = ndata;
11277           _bmi.bmiHeader.biWidth = (LONG)dimx;
11278           _bmi.bmiHeader.biHeight = -(int)dimy;
11279           _width = dimx;
11280           _height = dimy;
11281         }
11282         _window_width = dimx; _window_height = dimy;
11283         show();
11284       }
11285       _is_resized = false;
11286       if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2);
11287       if (force_redraw) return paint();
11288       return *this;
11289     }
11290 
11291     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
11292       if (is_empty()) return *this;
11293       if (force_redraw) {
11294         const cimg_ulong buf_size = (cimg_ulong)_width*_height*4;
11295         void *odata = std::malloc(buf_size);
11296         if (odata) {
11297           std::memcpy(odata,_data,buf_size);
11298           assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
11299           std::memcpy(_data,odata,buf_size);
11300           std::free(odata);
11301         }
11302         return paint();
11303       }
11304       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
11305     }
11306 
11307     CImgDisplay& show() {
11308       if (is_empty() || !_is_closed) return *this;
11309       _is_closed = false;
11310       if (_is_fullscreen) _init_fullscreen();
11311       ShowWindow(_window,SW_SHOW);
11312       _update_window_pos();
11313       return paint();
11314     }
11315 
11316     CImgDisplay& close() {
11317       if (is_empty() || _is_closed) return *this;
11318       _is_closed = true;
11319       if (_is_fullscreen) _desinit_fullscreen();
11320       ShowWindow(_window,SW_HIDE);
11321       _window_x = _window_y = cimg::type<int>::min();
11322       return *this;
11323     }
11324 
11325     CImgDisplay& move(const int posx, const int posy) {
11326       if (is_empty()) return *this;
11327       if (_window_x!=posx || _window_y!=posy) {
11328         SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
11329         _window_x = posx;
11330         _window_y = posy;
11331       }
11332       show();
11333       _is_moved = false;
11334       return *this;
11335     }
11336 
11337     CImgDisplay& show_mouse() {
11338       if (is_empty()) return *this;
11339       _is_cursor_visible = true;
11340       return *this;
11341     }
11342 
11343     CImgDisplay& hide_mouse() {
11344       if (is_empty()) return *this;
11345       _is_cursor_visible = false;
11346       return *this;
11347     }
11348 
11349     CImgDisplay& set_mouse(const int posx, const int posy) {
11350       if (is_empty() || _is_closed || posx<0 || posy<0) return *this;
11351       if (!_is_closed) {
11352         _update_window_pos();
11353         const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
11354         if (res) { _mouse_x = posx; _mouse_y = posy; }
11355       }
11356       return *this;
11357     }
11358 
11359     CImgDisplay& set_title(const char *const format, ...) {
11360       if (is_empty()) return *this;
11361       char *const tmp = new char[1024];
11362       va_list ap;
11363       va_start(ap, format);
11364       cimg_vsnprintf(tmp,1024,format,ap);
11365       va_end(ap);
11366       if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
11367       delete[] _title;
11368       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
11369       _title = new char[s];
11370       std::memcpy(_title,tmp,s*sizeof(char));
11371       SetWindowTextA(_window, tmp);
11372       delete[] tmp;
11373       return *this;
11374     }
11375 
11376     template<typename T>
11377     CImgDisplay& display(const CImg<T>& img) {
11378       if (!img)
11379         throw CImgArgumentException(_cimgdisplay_instance
11380                                     "display(): Empty specified image.",
11381                                     cimgdisplay_instance);
11382       if (is_empty()) return assign(img);
11383       return render(img).paint();
11384     }
11385 
11386     CImgDisplay& paint() {
11387       if (_is_closed) return *this;
11388       WaitForSingleObject(_mutex,INFINITE);
11389       SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
11390       ReleaseMutex(_mutex);
11391       return *this;
11392     }
11393 
11394     template<typename T>
11395     CImgDisplay& render(const CImg<T>& img) {
11396       if (!img)
11397         throw CImgArgumentException(_cimgdisplay_instance
11398                                     "render(): Empty specified image.",
11399                                     cimgdisplay_instance);
11400 
11401       if (is_empty()) return *this;
11402       if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
11403                                                              (img._depth - 1)/2));
11404 
11405       const T
11406         *data1 = img._data,
11407         *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
11408         *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
11409 
11410       WaitForSingleObject(_mutex,INFINITE);
11411       unsigned int
11412         *const ndata = (img._width==_width && img._height==_height)?_data:
11413         new unsigned int[(size_t)img._width*img._height],
11414         *ptrd = ndata;
11415 
11416       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
11417         _min = _max = 0;
11418         switch (img._spectrum) {
11419         case 1 : {
11420           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11421             const unsigned char val = (unsigned char)*(data1++);
11422             *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
11423           }
11424         } break;
11425         case 2 : {
11426           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11427             const unsigned char
11428               R = (unsigned char)*(data1++),
11429               G = (unsigned char)*(data2++);
11430             *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
11431           }
11432         } break;
11433         default : {
11434           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11435             const unsigned char
11436               R = (unsigned char)*(data1++),
11437               G = (unsigned char)*(data2++),
11438               B = (unsigned char)*(data3++);
11439             *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
11440           }
11441         }
11442         }
11443       } else {
11444         if (_normalization==3) {
11445           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
11446           else {
11447             _min = (float)cimg::type<T>::min();
11448             _max = (float)cimg::type<T>::max();
11449           }
11450         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
11451         const float delta = _max - _min, mm = 255/(delta?delta:1.f);
11452         switch (img._spectrum) {
11453         case 1 : {
11454           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11455             const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
11456             *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
11457           }
11458         } break;
11459         case 2 : {
11460           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11461             const unsigned char
11462               R = (unsigned char)((*(data1++) - _min)*mm),
11463               G = (unsigned char)((*(data2++) - _min)*mm);
11464             *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
11465           }
11466         } break;
11467         default : {
11468           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11469             const unsigned char
11470               R = (unsigned char)((*(data1++) - _min)*mm),
11471               G = (unsigned char)((*(data2++) - _min)*mm),
11472               B = (unsigned char)((*(data3++) - _min)*mm);
11473             *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
11474           }
11475         }
11476         }
11477       }
11478       if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
11479       ReleaseMutex(_mutex);
11480       return *this;
11481     }
11482 
11483     template<typename T>
11484     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
11485       img.assign();
11486       HDC hScreen = GetDC(GetDesktopWindow());
11487       if (hScreen) {
11488         const int
11489           width = GetDeviceCaps(hScreen,HORZRES),
11490           height = GetDeviceCaps(hScreen,VERTRES);
11491         int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
11492         if (_x0>_x1) cimg::swap(_x0,_x1);
11493         if (_y0>_y1) cimg::swap(_y0,_y1);
11494         if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
11495           _x0 = std::max(_x0,0);
11496           _y0 = std::max(_y0,0);
11497           _x1 = std::min(_x1,width - 1);
11498           _y1 = std::min(_y1,height - 1);
11499           const int bw = _x1 - _x0 + 1, bh = _y1 - _y0 + 1;
11500           HDC hdcMem = CreateCompatibleDC(hScreen);
11501           if (hdcMem) {
11502             HBITMAP hBitmap = CreateCompatibleBitmap(hScreen,bw,bh);
11503             if (hBitmap) {
11504               HGDIOBJ hOld = SelectObject(hdcMem,hBitmap);
11505               if (hOld && BitBlt(hdcMem,0,0,bw,bh,hScreen,_x0,_y0,SRCCOPY) && SelectObject(hdcMem,hOld)) {
11506                 BITMAPINFOHEADER bmi;
11507                 bmi.biSize = sizeof(BITMAPINFOHEADER);
11508                 bmi.biWidth = bw;
11509                 bmi.biHeight = -bh;
11510                 bmi.biPlanes = 1;
11511                 bmi.biBitCount = 32;
11512                 bmi.biCompression = BI_RGB;
11513                 bmi.biSizeImage = 0;
11514                 bmi.biXPelsPerMeter = bmi.biYPelsPerMeter = 0;
11515                 bmi.biClrUsed = bmi.biClrImportant = 0;
11516                 unsigned char *buf = new unsigned char[4*bw*bh];
11517                 if (GetDIBits(hdcMem,hBitmap,0,bh,buf,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)) {
11518                   img.assign(bw,bh,1,3);
11519                   const unsigned char *ptrs = buf;
11520                   T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
11521                   cimg_forXY(img,x,y) {
11522                     *(pR++) = (T)ptrs[2];
11523                     *(pG++) = (T)ptrs[1];
11524                     *(pB++) = (T)ptrs[0];
11525                     ptrs+=4;
11526                   }
11527                 }
11528                 delete[] buf;
11529               }
11530               DeleteObject(hBitmap);
11531             }
11532             DeleteDC(hdcMem);
11533           }
11534         }
11535         ReleaseDC(GetDesktopWindow(),hScreen);
11536       }
11537       if (img.is_empty())
11538         throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
11539                                    "with coordinates (%d,%d)-(%d,%d).",
11540                                    x0,y0,x1,y1);
11541     }
11542 
11543     template<typename T>
11544     const CImgDisplay& snapshot(CImg<T>& img) const {
11545       if (is_empty()) { img.assign(); return *this; }
11546       const unsigned int *ptrs = _data;
11547       img.assign(_width,_height,1,3);
11548       T
11549         *data1 = img.data(0,0,0,0),
11550         *data2 = img.data(0,0,0,1),
11551         *data3 = img.data(0,0,0,2);
11552       for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
11553         const unsigned int val = *(ptrs++);
11554         *(data1++) = (T)(unsigned char)(val>>16);
11555         *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
11556         *(data3++) = (T)(unsigned char)(val&0xFF);
11557       }
11558       return *this;
11559     }
11560 #endif
11561 
11562     //@}
11563   }; // struct CImgDisplay { ...
11564 
11565   /*
11566    #--------------------------------------
11567    #
11568    #
11569    #
11570    # Definition of the CImg<T> structure
11571    #
11572    #
11573    #
11574    #--------------------------------------
11575    */
11576 
11577   //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
11578   /**
11579      This is the main class of the %CImg Library. It declares and constructs
11580      an image, allows access to its pixel values, and is able to perform various image operations.
11581 
11582      \par Image representation
11583 
11584      A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
11585      each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth
11586      and number of channels.
11587      Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>,
11588      while the number of channels is rather used as a vector-valued dimension
11589      (it may describe the R,G,B color channels for instance).
11590      If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
11591 
11592      Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
11593      as well as images with less dimensions (1D scalar signal, 2D color images, ...).
11594      Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
11595 
11596      Concerning the pixel value type \c T:
11597      fully supported template types are the basic C++ types: <tt>unsigned char, char, short, unsigned int, int,
11598      unsigned long, long, float, double, ... </tt>.
11599      Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
11600      while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
11601      images that have floating-point pixel values. The default value for the template T is \c float.
11602      Using your own template types may be possible. However, you will certainly have to define the complete set
11603      of arithmetic and logical operators for your class.
11604 
11605      \par Image structure
11606 
11607      The \c CImg<T> structure contains \e six fields:
11608      - \c _width defines the number of \a columns of the image (size along the X-axis).
11609      - \c _height defines the number of \a rows of the image (size along the Y-axis).
11610      - \c _depth defines the number of \a slices of the image (size along the Z-axis).
11611      - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
11612      - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
11613      - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
11614        another image.
11615 
11616      You can access these fields publicly although it is recommended to use the dedicated functions
11617      width(), height(), depth(), spectrum() and ptr() to do so.
11618      Image dimensions are not limited to a specific range (as long as you got enough available memory).
11619      A value of \e 1 usually means that the corresponding dimension is \a flat.
11620      If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
11621      Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
11622      (a CImgInstanceException will be thrown instead).
11623      Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
11624 
11625      \par Image declaration and construction
11626 
11627      Declaring an image can be done by using one of the several available constructors.
11628      Here is a list of the most used:
11629 
11630      - Construct images from arbitrary dimensions:
11631          - <tt>CImg<char> img;</tt> declares an empty image.
11632          - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
11633          \c unsigned \c char pixel values.
11634          - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
11635          - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
11636          (colors are stored as an image with three channels).
11637          - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
11638          (with \c double pixel values).
11639          - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
11640          (with \c float pixels, which is the default value of the template parameter \c T).
11641          - \b Note: images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
11642          do it, or use the specific constructor taking 5 parameters like this:
11643          <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
11644 
11645      - Construct images from filenames:
11646          - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
11647          - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the
11648          file "analyze.hdr".
11649          - \b Note: You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
11650          to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
11651 
11652      - Construct images from C-style arrays:
11653          - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
11654          \c data_buffer (of size 256x256=65536).
11655          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3);</tt> constructs a 256x256 color image
11656          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
11657 
11658          The complete list of constructors can be found <a href="#constructors">here</a>.
11659 
11660      \par Most useful functions
11661 
11662      The \c CImg<T> class contains a lot of functions that operates on images.
11663      Some of the most useful are:
11664 
11665      - operator()(): Read or write pixel values.
11666      - display(): displays the image in a new window.
11667   **/
11668   template<typename T>
11669   struct CImg {
11670 
11671     unsigned int _width, _height, _depth, _spectrum;
11672     bool _is_shared;
11673     T *_data;
11674 
11675     //! Simple iterator type, to loop through each pixel value of an image instance.
11676     /**
11677        \note
11678        - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
11679        - You will seldom have to use iterators in %CImg, most classical operations
11680          being achieved (often in a faster way) using methods of \c CImg<T>.
11681        \par Example
11682        \code
11683        CImg<float> img("reference.jpg");                                         // Load image from file
11684        // Set all pixels to '0', with a CImg iterator.
11685        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0;
11686        img.fill(0);                                                              // Do the same with a built-in method
11687        \endcode
11688    **/
11689     typedef T* iterator;
11690 
11691     //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
11692     /**
11693        \note
11694        - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
11695        - You will seldom have to use iterators in %CImg, most classical operations
11696          being achieved (often in a faster way) using methods of \c CImg<T>.
11697        \par Example
11698        \code
11699        const CImg<float> img("reference.jpg");                                    // Load image from file
11700        float sum = 0;
11701        // Compute sum of all pixel values, with a CImg iterator.
11702        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it;
11703        const float sum2 = img.sum();                                              // Do the same with a built-in method
11704        \endcode
11705     **/
11706     typedef const T* const_iterator;
11707 
11708     //! Pixel value type.
11709     /**
11710        Refer to the type of the pixel values of an image instance.
11711        \note
11712        - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
11713        - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
11714          compatibility with STL naming conventions.
11715     **/
11716     typedef T value_type;
11717 
11718     // Define common types related to template type T.
11719     typedef typename cimg::superset<T,bool>::type Tbool;
11720     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
11721     typedef typename cimg::superset<T,char>::type Tchar;
11722     typedef typename cimg::superset<T,unsigned short>::type Tushort;
11723     typedef typename cimg::superset<T,short>::type Tshort;
11724     typedef typename cimg::superset<T,unsigned int>::type Tuint;
11725     typedef typename cimg::superset<T,int>::type Tint;
11726     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
11727     typedef typename cimg::superset<T,cimg_long>::type Tlong;
11728     typedef typename cimg::superset<T,float>::type Tfloat;
11729     typedef typename cimg::superset<T,double>::type Tdouble;
11730     typedef typename cimg::last<T,bool>::type boolT;
11731     typedef typename cimg::last<T,unsigned char>::type ucharT;
11732     typedef typename cimg::last<T,char>::type charT;
11733     typedef typename cimg::last<T,unsigned short>::type ushortT;
11734     typedef typename cimg::last<T,short>::type shortT;
11735     typedef typename cimg::last<T,unsigned int>::type uintT;
11736     typedef typename cimg::last<T,int>::type intT;
11737     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
11738     typedef typename cimg::last<T,cimg_long>::type longT;
11739     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
11740     typedef typename cimg::last<T,cimg_int64>::type int64T;
11741     typedef typename cimg::last<T,float>::type floatT;
11742     typedef typename cimg::last<T,double>::type doubleT;
11743 
11744     // Return 'dx*dy*dz*dc' as a 'size_t' and check no overflow occurs.
11745     static size_t safe_size(const unsigned int dx, const unsigned int dy,
11746                             const unsigned int dz, const unsigned int dc) {
11747       if (!(dx && dy && dz && dc)) return 0;
11748       size_t siz = (size_t)dx, osiz = siz;
11749       if ((dy==1 || (siz*=dy)>osiz) &&
11750           ((osiz = siz), dz==1 || (siz*=dz)>osiz) &&
11751           ((osiz = siz), dc==1 || (siz*=dc)>osiz) &&
11752           ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) return siz;
11753       throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) overflows 'size_t'.",
11754                                   pixel_type(),dx,dy,dz,dc);
11755     }
11756 
11757     //@}
11758     //---------------------------
11759     //
11760     //! \name Plugins
11761     //@{
11762     //---------------------------
11763 #ifdef cimg_plugin
11764 #include cimg_plugin
11765 #endif
11766 #ifdef cimg_plugin1
11767 #include cimg_plugin1
11768 #endif
11769 #ifdef cimg_plugin2
11770 #include cimg_plugin2
11771 #endif
11772 #ifdef cimg_plugin3
11773 #include cimg_plugin3
11774 #endif
11775 #ifdef cimg_plugin4
11776 #include cimg_plugin4
11777 #endif
11778 #ifdef cimg_plugin5
11779 #include cimg_plugin5
11780 #endif
11781 #ifdef cimg_plugin6
11782 #include cimg_plugin6
11783 #endif
11784 #ifdef cimg_plugin7
11785 #include cimg_plugin7
11786 #endif
11787 #ifdef cimg_plugin8
11788 #include cimg_plugin8
11789 #endif
11790 
11791     //@}
11792     //---------------------------------------------------------
11793     //
11794     //! \name Constructors / Destructor / Instance Management
11795     //@{
11796     //---------------------------------------------------------
11797 
11798     //! Destroy image.
11799     /**
11800        \note
11801        - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
11802        - Destroying an empty or shared image does nothing actually.
11803        \warning
11804        - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
11805          that shares its buffer with the destroyed instance, in order to avoid further invalid memory access
11806          (to a deallocated buffer).
11807     **/
11808     ~CImg() {
11809       if (!_is_shared) delete[] _data;
11810     }
11811 
11812     //! Construct empty image.
11813     /**
11814        \note
11815        - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
11816          are set to \c 0, as well as its pixel buffer pointer data().
11817        - An empty image may be re-assigned afterwards, e.g. with the family of
11818          assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
11819          or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
11820        - An empty image is never shared.
11821        \par Example
11822        \code
11823        CImg<float> img1, img2;      // Construct two empty images
11824        img1.assign(256,256,1,3);    // Re-assign 'img1' to be a 256x256x1x3 (color) image
11825        img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'
11826        img2.assign();               // Re-assign 'img2' to be an empty image again
11827        \endcode
11828     **/
11829     CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
11830 
11831     //! Construct image with specified size.
11832     /**
11833        \param size_x Image width().
11834        \param size_y Image height().
11835        \param size_z Image depth().
11836        \param size_c Image spectrum() (number of channels).
11837        \note
11838        - It is able to create only \e non-shared images, and allocates thus a pixel buffer data()
11839          for each constructed image instance.
11840        - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of
11841          an \e empty image.
11842        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
11843          (e.g. when requested size is too big for available memory).
11844        \warning
11845        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
11846          In order to initialize pixel values during construction (e.g. with \c 0), use constructor
11847          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
11848        \par Example
11849        \code
11850        CImg<float> img1(256,256,1,3);   // Construct a 256x256x1x3 (color) image, filled with garbage values
11851        CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'
11852        \endcode
11853     **/
11854     explicit CImg(const unsigned int size_x, const unsigned int size_y=1,
11855                   const unsigned int size_z=1, const unsigned int size_c=1):
11856       _is_shared(false) {
11857       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
11858       if (siz) {
11859         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11860         try { _data = new T[siz]; } catch (...) {
11861           _width = _height = _depth = _spectrum = 0; _data = 0;
11862           throw CImgInstanceException(_cimg_instance
11863                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11864                                       cimg_instance,
11865                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11866                                       size_x,size_y,size_z,size_c);
11867         }
11868       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11869     }
11870 
11871     //! Construct image with specified size and initialize pixel values.
11872     /**
11873        \param size_x Image width().
11874        \param size_y Image height().
11875        \param size_z Image depth().
11876        \param size_c Image spectrum() (number of channels).
11877        \param value Initialization value.
11878        \note
11879        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
11880          but it also fills the pixel buffer with the specified \c value.
11881        \warning
11882        - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels
11883          (e.g. RGB vector, for color images).
11884          For this task, you may use fillC() after construction.
11885     **/
11886     CImg(const unsigned int size_x, const unsigned int size_y,
11887          const unsigned int size_z, const unsigned int size_c, const T& value):
11888       _is_shared(false) {
11889       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
11890       if (siz) {
11891         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11892         try { _data = new T[siz]; } catch (...) {
11893           _width = _height = _depth = _spectrum = 0; _data = 0;
11894           throw CImgInstanceException(_cimg_instance
11895                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11896                                       cimg_instance,
11897                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11898                                       size_x,size_y,size_z,size_c);
11899         }
11900         fill(value);
11901       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11902     }
11903 
11904     //! Construct image with specified size and initialize pixel values from a sequence of integers.
11905     /**
11906        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
11907        with pixels of type \c T, and initialize pixel
11908        values from the specified sequence of integers \c value0,\c value1,\c ...
11909        \param size_x Image width().
11910        \param size_y Image height().
11911        \param size_z Image depth().
11912        \param size_c Image spectrum() (number of channels).
11913        \param value0 First value of the initialization sequence (must be an \e integer).
11914        \param value1 Second value of the initialization sequence (must be an \e integer).
11915        \param ...
11916        \note
11917        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
11918          the pixel buffer with a sequence of specified integer values.
11919        \warning
11920        - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
11921          Otherwise, the constructor may crash or fill your image pixels with garbage.
11922        \par Example
11923        \code
11924        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image
11925                              0,255,0,255,  // Set the 4 values for the red component
11926                              0,0,255,255,  // Set the 4 values for the green component
11927                              64,64,64,64); // Set the 4 values for the blue component
11928        img.resize(150,150).display();
11929        \endcode
11930        \image html ref_constructor1.jpg
11931      **/
11932     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
11933          const int value0, const int value1, ...):
11934       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11935 #define _CImg_stdarg(img,a0,a1,N,t) { \
11936         size_t _siz = (size_t)N; \
11937         if (_siz--) { \
11938           va_list ap; \
11939           va_start(ap,a1); \
11940           T *ptrd = (img)._data; \
11941           *(ptrd++) = (T)a0; \
11942           if (_siz--) { \
11943             *(ptrd++) = (T)a1; \
11944             for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
11945           } \
11946           va_end(ap); \
11947         } \
11948       }
11949       assign(size_x,size_y,size_z,size_c);
11950       _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int);
11951     }
11952 
11953 #if cimg_use_cpp11==1
11954     //! Construct image with specified size and initialize pixel values from an initializer list of integers.
11955     /**
11956        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
11957        with pixels of type \c T, and initialize pixel
11958        values from the specified initializer list of integers { \c value0,\c value1,\c ... }
11959        \param size_x Image width().
11960        \param size_y Image height().
11961        \param size_z Image depth().
11962        \param size_c Image spectrum() (number of channels).
11963        \param { value0, value1, ... } Initialization list
11964        \param repeat_values Tells if the value filling process is repeated over the image.
11965 
11966        \note
11967        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
11968          the pixel buffer with a sequence of specified integer values.
11969        \par Example
11970        \code
11971        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image
11972                              { 0,255,0,255,    // Set the 4 values for the red component
11973                                0,0,255,255,    // Set the 4 values for the green component
11974                                64,64,64,64 }); // Set the 4 values for the blue component
11975        img.resize(150,150).display();
11976        \endcode
11977        \image html ref_constructor1.jpg
11978     **/
11979     template<typename t>
11980     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
11981          const std::initializer_list<t> values,
11982          const bool repeat_values=true):
11983       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11984 #define _cimg_constructor_cpp11(repeat_values) \
11985   auto it = values.begin(); \
11986   size_t siz = size(); \
11987   if (repeat_values) for (T *ptrd = _data; siz--; ) { \
11988     *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \
11989   else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); }
11990       assign(size_x,size_y,size_z,size_c);
11991       _cimg_constructor_cpp11(repeat_values);
11992     }
11993 
11994     template<typename t>
11995     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z,
11996          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       assign(size_x,size_y,size_z);
12000       _cimg_constructor_cpp11(repeat_values);
12001     }
12002 
12003     template<typename t>
12004     CImg(const unsigned int size_x, const unsigned int size_y,
12005          std::initializer_list<t> values,
12006          const bool repeat_values=true):
12007       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12008       assign(size_x,size_y);
12009       _cimg_constructor_cpp11(repeat_values);
12010     }
12011 
12012     template<typename t>
12013     CImg(const unsigned int size_x,
12014          std::initializer_list<t> values,
12015          const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12016       assign(size_x);
12017       _cimg_constructor_cpp11(repeat_values);
12018     }
12019 
12020     //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers.
12021     /**
12022        Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1,
12023        with pixels of type \c T, and initialize pixel
12024        values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is
12025        given by the size of the initializer list.
12026        \param { value0, value1, ... } Initialization list
12027        \note
12028        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1,
12029          but it also fills the pixel buffer with a sequence of specified integer values.
12030        \par Example
12031        \code
12032        const CImg<float> img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values
12033        img.resize(150,150).display();
12034        \endcode
12035        \image html ref_constructor1.jpg
12036      **/
12037     template<typename t>
12038     CImg(const std::initializer_list<t> values):
12039       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12040       assign(values.size(),1,1,1);
12041       auto it = values.begin();
12042       unsigned int siz = _width;
12043       for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++));
12044     }
12045 
12046     template<typename t>
12047     CImg<T>& operator=(std::initializer_list<t> values) {
12048       _cimg_constructor_cpp11(siz>values.size());
12049       return *this;
12050     }
12051 #endif
12052 
12053     //! Construct image with specified size and initialize pixel values from a sequence of doubles.
12054     /**
12055        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,
12056        and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ...
12057        \param size_x Image width().
12058        \param size_y Image height().
12059        \param size_z Image depth().
12060        \param size_c Image spectrum() (number of channels).
12061        \param value0 First value of the initialization sequence (must be a \e double).
12062        \param value1 Second value of the initialization sequence (must be a \e double).
12063        \param ...
12064        \note
12065        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
12066          takes a sequence of double values instead of integers.
12067        \warning
12068        - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
12069          Otherwise, the constructor may crash or fill your image with garbage.
12070          For instance, the code below will probably crash on most platforms:
12071          \code
12072          const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'!
12073          \endcode
12074      **/
12075     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
12076          const double value0, const double value1, ...):
12077       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12078       assign(size_x,size_y,size_z,size_c);
12079       _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double);
12080     }
12081 
12082     //! Construct image with specified size and initialize pixel values from a value string.
12083     /**
12084        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,
12085        and initializes pixel values from the specified string \c values.
12086        \param size_x Image width().
12087        \param size_y Image height().
12088        \param size_z Image depth().
12089        \param size_c Image spectrum() (number of channels).
12090        \param values Value string describing the way pixel values are set.
12091        \param repeat_values Tells if the value filling process is repeated over the image.
12092        \note
12093        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
12094          the pixel buffer with values described in the value string \c values.
12095        - Value string \c values may describe two different filling processes:
12096          - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
12097            In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
12098          - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>.
12099            In this case, parameter \c repeat_values is pointless.
12100        - For both cases, specifying \c repeat_values is mandatory.
12101          It disambiguates the possible overloading of constructor
12102          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
12103        - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
12104        \par Example
12105        \code
12106        const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence
12107                          img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula
12108        (img1,img2).display();
12109        \endcode
12110        \image html ref_constructor2.jpg
12111      **/
12112     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
12113          const char *const values, const bool repeat_values):_is_shared(false) {
12114       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12115       if (siz) {
12116         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
12117         try { _data = new T[siz]; } catch (...) {
12118           _width = _height = _depth = _spectrum = 0; _data = 0;
12119           throw CImgInstanceException(_cimg_instance
12120                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12121                                       cimg_instance,
12122                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12123                                       size_x,size_y,size_z,size_c);
12124         }
12125         fill(values,repeat_values);
12126       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
12127     }
12128 
12129     //! Construct image with specified size and initialize pixel values from a memory buffer.
12130     /**
12131        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,
12132        and initializes pixel values from the specified \c t* memory buffer.
12133        \param values Pointer to the input memory buffer.
12134        \param size_x Image width().
12135        \param size_y Image height().
12136        \param size_z Image depth().
12137        \param size_c Image spectrum() (number of channels).
12138        \param is_shared Tells if input memory buffer must be shared by the current instance.
12139        \note
12140        - If \c is_shared is \c false, the image instance allocates its own pixel buffer,
12141          and values from the specified input buffer are copied to the instance buffer.
12142          If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy.
12143        - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its
12144          own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared
12145          image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator.
12146        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
12147          (e.g. when requested size is too big for available memory).
12148        \warning
12149        - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data()
12150          (e.g. already deallocated).
12151        \par Example
12152        \code
12153        unsigned char tab[256*256] = { 0 };
12154        CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'
12155                            img2(tab,256,256,1,1,true);  // Construct new shared-image from buffer 'tab'
12156        tab[1024] = 255;                                 // Here, 'img2' is indirectly modified, but not 'img1'
12157        \endcode
12158     **/
12159     template<typename t>
12160     CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
12161          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
12162       if (is_shared) {
12163         _width = _height = _depth = _spectrum = 0; _data = 0;
12164         throw CImgArgumentException(_cimg_instance
12165                                     "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance "
12166                                     "from a (%s*) buffer (pixel types are different).",
12167                                     cimg_instance,
12168                                     size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
12169       }
12170       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12171       if (values && siz) {
12172         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
12173         try { _data = new T[siz]; } catch (...) {
12174           _width = _height = _depth = _spectrum = 0; _data = 0;
12175           throw CImgInstanceException(_cimg_instance
12176                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12177                                       cimg_instance,
12178                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12179                                       size_x,size_y,size_z,size_c);
12180 
12181         }
12182         const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
12183       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
12184     }
12185 
12186     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
12187     CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
12188          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
12189       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12190       if (values && siz) {
12191         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
12192         if (_is_shared) _data = const_cast<T*>(values);
12193         else {
12194           try { _data = new T[siz]; } catch (...) {
12195             _width = _height = _depth = _spectrum = 0; _data = 0;
12196             throw CImgInstanceException(_cimg_instance
12197                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12198                                         cimg_instance,
12199                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12200                                         size_x,size_y,size_z,size_c);
12201           }
12202           std::memcpy(_data,values,siz*sizeof(T));
12203         }
12204       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
12205     }
12206 
12207     //! Construct image from memory buffer with specified size and pixel ordering scheme.
12208     template<typename t>
12209     CImg(const t *const values, const unsigned int size_x, const unsigned int size_y,
12210          const unsigned int size_z, const unsigned int size_c,
12211          const char *const axes_order):_data(0),_is_shared(false) {
12212       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12213       if (values && siz) {
12214         unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 };
12215         for (unsigned int l = 0; axes_order[l]; ++l) {
12216           int c = cimg::lowercase(axes_order[l]);
12217           if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; }
12218           else { ++n_code[c%=4]; s_code[l] = c; }
12219         }
12220         if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
12221           const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
12222           int s0 = 0, s1 = 0, s2 = 0, s3 = 0;
12223           const char *inv_order = 0;
12224           switch (code) {
12225             case 0x0123 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_z; s3 = size_c; break; // xyzc
12226             case 0x0132 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_c; s3 = size_z; break; // xycz
12227             case 0x0213 : inv_order = "xzyc"; s0 = size_x; s1 = size_z; s2 = size_y; s3 = size_c; break; // xzyc
12228             case 0x0231 : inv_order = "xcyz"; s0 = size_x; s1 = size_z; s2 = size_c; s3 = size_y; break; // xzcy
12229             case 0x0312 : inv_order = "xzcy"; s0 = size_x; s1 = size_c; s2 = size_y; s3 = size_z; break; // xcyz
12230             case 0x0321 : inv_order = "xczy"; s0 = size_x; s1 = size_c; s2 = size_z; s3 = size_y; break; // xczy
12231             case 0x1023 : inv_order = "yxzc"; s0 = size_y; s1 = size_x; s2 = size_z; s3 = size_c; break; // yxzc
12232             case 0x1032 : inv_order = "yxcz"; s0 = size_y; s1 = size_x; s2 = size_c; s3 = size_z; break; // yxcz
12233             case 0x1203 : inv_order = "zxyc"; s0 = size_y; s1 = size_z; s2 = size_x; s3 = size_c; break; // yzxc
12234             case 0x1230 : inv_order = "cxyz"; s0 = size_y; s1 = size_z; s2 = size_c; s3 = size_x; break; // yzcx
12235             case 0x1302 : inv_order = "zxcy"; s0 = size_y; s1 = size_c; s2 = size_x; s3 = size_z; break; // ycxz
12236             case 0x1320 : inv_order = "cxzy"; s0 = size_y; s1 = size_c; s2 = size_z; s3 = size_x; break; // yczx
12237             case 0x2013 : inv_order = "yzxc"; s0 = size_z; s1 = size_x; s2 = size_y; s3 = size_c; break; // zxyc
12238             case 0x2031 : inv_order = "ycxz"; s0 = size_z; s1 = size_x; s2 = size_c; s3 = size_y; break; // zxcy
12239             case 0x2103 : inv_order = "zyxc"; s0 = size_z; s1 = size_y; s2 = size_x; s3 = size_c; break; // zyxc
12240             case 0x2130 : inv_order = "cyxz"; s0 = size_z; s1 = size_y; s2 = size_c; s3 = size_x; break; // zycx
12241             case 0x2301 : inv_order = "zcxy"; s0 = size_z; s1 = size_c; s2 = size_x; s3 = size_y; break; // zcxy
12242             case 0x2310 : inv_order = "czxy"; s0 = size_z; s1 = size_c; s2 = size_y; s3 = size_x; break; // zcyx
12243             case 0x3012 : inv_order = "yzcx"; s0 = size_c; s1 = size_x; s2 = size_y; s3 = size_z; break; // cxyz
12244             case 0x3021 : inv_order = "yczx"; s0 = size_c; s1 = size_x; s2 = size_z; s3 = size_y; break; // cxzy
12245             case 0x3102 : inv_order = "zycx"; s0 = size_c; s1 = size_y; s2 = size_x; s3 = size_z; break; // cyxz
12246             case 0x3120 : inv_order = "cyzx"; s0 = size_c; s1 = size_y; s2 = size_z; s3 = size_x; break; // cyzx
12247             case 0x3201 : inv_order = "zcyx"; s0 = size_c; s1 = size_z; s2 = size_x; s3 = size_y; break; // czxy
12248             case 0x3210 : inv_order = "czyx"; s0 = size_c; s1 = size_z; s2 = size_y; s3 = size_x; break; // czyx
12249           }
12250           CImg<t>(values,s0,s1,s2,s3,true).get_permute_axes(inv_order).move_to(*this);
12251         } else {
12252           _width = _height = _depth = _spectrum = 0; _data = 0;
12253           throw CImgArgumentException(_cimg_instance
12254                                       "CImg(): Invalid specified axes order '%s'.",
12255                                       cimg_instance,
12256                                       axes_order);
12257         }
12258       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
12259     }
12260 
12261     //! Construct image from reading an image file.
12262     /**
12263        Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from
12264        an image file.
12265        \param filename Filename, as a C-string.
12266        \note
12267        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
12268          dimensions and pixel values from the specified image file.
12269        - The recognition of the image file format by %CImg higlhy depends on the tools installed on your system
12270          and on the external libraries you used to link your code against.
12271        - Considered pixel type \c T should better fit the file format specification, or data loss may occur during
12272          file load (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
12273        - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not
12274          recognized.
12275        \par Example
12276        \code
12277        const CImg<float> img("reference.jpg");
12278        img.display();
12279        \endcode
12280        \image html ref_image.jpg
12281     **/
12282     explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12283       assign(filename);
12284     }
12285 
12286     //! Construct image copy.
12287     /**
12288        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
12289        \param img Input image to copy.
12290        \note
12291        - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the
12292          input image \c img.
12293        - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also
12294          \e shared, and shares its pixel buffer with \c img.
12295          Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
12296          This behavior is needful to allow functions to return shared images.
12297        - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input
12298          image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and
12299          \c t are different.
12300        - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than
12301          with different types.
12302        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
12303          (e.g. not enough available memory).
12304     **/
12305     template<typename t>
12306     CImg(const CImg<t>& img):_is_shared(false) {
12307       const size_t siz = (size_t)img.size();
12308       if (img._data && siz) {
12309         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
12310         try { _data = new T[siz]; } catch (...) {
12311           _width = _height = _depth = _spectrum = 0; _data = 0;
12312           throw CImgInstanceException(_cimg_instance
12313                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12314                                       cimg_instance,
12315                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
12316                                       img._width,img._height,img._depth,img._spectrum);
12317         }
12318         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
12319       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
12320     }
12321 
12322     //! Construct image copy \specialization.
12323     CImg(const CImg<T>& img) {
12324       const size_t siz = (size_t)img.size();
12325       if (img._data && siz) {
12326         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
12327         _is_shared = img._is_shared;
12328         if (_is_shared) _data = const_cast<T*>(img._data);
12329         else {
12330           try { _data = new T[siz]; } catch (...) {
12331             _width = _height = _depth = _spectrum = 0; _data = 0;
12332             throw CImgInstanceException(_cimg_instance
12333                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12334                                         cimg_instance,
12335                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
12336                                         img._width,img._height,img._depth,img._spectrum);
12337 
12338           }
12339           std::memcpy(_data,img._data,siz*sizeof(T));
12340         }
12341       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
12342     }
12343 
12344     //! Advanced copy constructor.
12345     /**
12346        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
12347        while forcing the shared state of the constructed copy.
12348        \param img Input image to copy.
12349        \param is_shared Tells about the shared state of the constructed copy.
12350        \note
12351        - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
12352          the constructed image, which does not depend anymore on the shared state of the input image \c img:
12353          - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
12354            For that case, the pixel types \c T and \c t \e must be the same.
12355          - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input
12356            image \c img is shared or not.
12357        - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
12358     **/
12359     template<typename t>
12360     CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
12361       if (is_shared) {
12362         _width = _height = _depth = _spectrum = 0; _data = 0;
12363         throw CImgArgumentException(_cimg_instance
12364                                     "CImg(): Invalid construction request of a shared instance from a "
12365                                     "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
12366                                     cimg_instance,
12367                                     CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
12368       }
12369       const size_t siz = (size_t)img.size();
12370       if (img._data && siz) {
12371         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
12372         try { _data = new T[siz]; } catch (...) {
12373           _width = _height = _depth = _spectrum = 0; _data = 0;
12374           throw CImgInstanceException(_cimg_instance
12375                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12376                                       cimg_instance,
12377                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
12378                                       img._width,img._height,img._depth,img._spectrum);
12379         }
12380         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
12381       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
12382     }
12383 
12384     //! Advanced copy constructor \specialization.
12385     CImg(const CImg<T>& img, const bool is_shared) {
12386       const size_t siz = (size_t)img.size();
12387       if (img._data && siz) {
12388         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
12389         _is_shared = is_shared;
12390         if (_is_shared) _data = const_cast<T*>(img._data);
12391         else {
12392           try { _data = new T[siz]; } catch (...) {
12393             _width = _height = _depth = _spectrum = 0; _data = 0;
12394             throw CImgInstanceException(_cimg_instance
12395                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12396                                         cimg_instance,
12397                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
12398                                         img._width,img._height,img._depth,img._spectrum);
12399           }
12400           std::memcpy(_data,img._data,siz*sizeof(T));
12401         }
12402       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
12403     }
12404 
12405     //! Construct image with dimensions borrowed from another image.
12406     /**
12407        Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing
12408        \c CImg<t> instance.
12409        \param img Input image from which dimensions are borrowed.
12410        \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions.
12411        \note
12412        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
12413          (\e not its pixel values) from an existing \c CImg<t> instance.
12414        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
12415          In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T)
12416          instead.
12417        \par Example
12418        \code
12419        const CImg<float> img1(256,128,1,3),      // 'img1' is a 256x128x1x3 image
12420                          img2(img1,"xyzc"),      // 'img2' is a 256x128x1x3 image
12421                          img3(img1,"y,x,z,c"),   // 'img3' is a 128x256x1x3 image
12422                          img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0')
12423        \endcode
12424      **/
12425     template<typename t>
12426     CImg(const CImg<t>& img, const char *const dimensions):
12427       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12428       assign(img,dimensions);
12429     }
12430 
12431     //! Construct image with dimensions borrowed from another image and initialize pixel values.
12432     /**
12433        Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing
12434        \c CImg<t> instance, and set all pixel values to specified \c value.
12435        \param img Input image from which dimensions are borrowed.
12436        \param dimensions String describing the image size along the X,Y,Z and V-dimensions.
12437        \param value Value used for initialization.
12438        \note
12439        - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
12440      **/
12441     template<typename t>
12442     CImg(const CImg<t>& img, const char *const dimensions, const T& value):
12443       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12444       assign(img,dimensions).fill(value);
12445     }
12446 
12447     //! Construct image from a display window.
12448     /**
12449        Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
12450        \param disp Input display window.
12451        \note
12452        - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
12453        - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3
12454          (i.e. a 2D color image).
12455        - The image pixels are read as 8-bits RGB values.
12456      **/
12457     explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12458       disp.snapshot(*this);
12459     }
12460 
12461     // Constructor and assignment operator for rvalue references (c++11).
12462     // This avoids an additional image copy for methods returning new images. Can save RAM for big images !
12463 #if cimg_use_cpp11==1
12464     CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
12465       swap(img);
12466     }
12467 
12468     CImg<T>& operator=(CImg<T>&& img) {
12469       if (_is_shared) return assign(img);
12470       return img.swap(*this);
12471     }
12472 #endif
12473 
12474     //! Construct empty image \inplace.
12475     /**
12476        In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
12477     **/
12478     CImg<T>& assign() {
12479       if (!_is_shared) delete[] _data;
12480       _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
12481       return *this;
12482     }
12483 
12484     //! Construct image with specified size \inplace.
12485     /**
12486        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
12487     **/
12488     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1,
12489                     const unsigned int size_z=1, const unsigned int size_c=1) {
12490       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12491       if (!siz) return assign();
12492       const size_t curr_siz = (size_t)size();
12493       if (siz!=curr_siz) {
12494         if (_is_shared)
12495           throw CImgArgumentException(_cimg_instance
12496                                       "assign(): Invalid assignment request of shared instance from specified "
12497                                       "image (%u,%u,%u,%u).",
12498                                       cimg_instance,
12499                                       size_x,size_y,size_z,size_c);
12500         else {
12501           delete[] _data;
12502           try { _data = new T[siz]; } catch (...) {
12503             _width = _height = _depth = _spectrum = 0; _data = 0;
12504             throw CImgInstanceException(_cimg_instance
12505                                         "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12506                                         cimg_instance,
12507                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12508                                         size_x,size_y,size_z,size_c);
12509           }
12510         }
12511       }
12512       _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
12513       return *this;
12514     }
12515 
12516     //! Construct image with specified size and initialize pixel values \inplace.
12517     /**
12518        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
12519     **/
12520     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
12521                     const unsigned int size_z, const unsigned int size_c, const T& value) {
12522       return assign(size_x,size_y,size_z,size_c).fill(value);
12523     }
12524 
12525     //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace.
12526     /**
12527        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
12528     **/
12529     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
12530                     const unsigned int size_z, const unsigned int size_c,
12531                     const int value0, const int value1, ...) {
12532       assign(size_x,size_y,size_z,size_c);
12533       _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int);
12534       return *this;
12535     }
12536 
12537     //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace.
12538     /**
12539        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
12540     **/
12541     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
12542                     const unsigned int size_z, const unsigned int size_c,
12543                     const double value0, const double value1, ...) {
12544       assign(size_x,size_y,size_z,size_c);
12545       _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double);
12546       return *this;
12547     }
12548 
12549     //! Construct image with specified size and initialize pixel values from a value string \inplace.
12550     /**
12551        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
12552     **/
12553     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
12554                     const unsigned int size_z, const unsigned int size_c,
12555                     const char *const values, const bool repeat_values) {
12556       return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
12557     }
12558 
12559     //! Construct image with specified size and initialize pixel values from a memory buffer \inplace.
12560     /**
12561        In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
12562     **/
12563     template<typename t>
12564     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
12565                     const unsigned int size_z=1, const unsigned int size_c=1) {
12566       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12567       if (!values || !siz) return assign();
12568       assign(size_x,size_y,size_z,size_c);
12569       const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
12570       return *this;
12571     }
12572 
12573     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
12574     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
12575                     const unsigned int size_z=1, const unsigned int size_c=1) {
12576       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12577       if (!values || !siz) return assign();
12578       const size_t curr_siz = (size_t)size();
12579       if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
12580       if (_is_shared || values + siz<_data || values>=_data + size()) {
12581         assign(size_x,size_y,size_z,size_c);
12582         if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T));
12583         else std::memcpy((void*)_data,(void*)values,siz*sizeof(T));
12584       } else {
12585         T *new_data = 0;
12586         try { new_data = new T[siz]; } catch (...) {
12587           _width = _height = _depth = _spectrum = 0; _data = 0;
12588           throw CImgInstanceException(_cimg_instance
12589                                       "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
12590                                       cimg_instance,
12591                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
12592                                       size_x,size_y,size_z,size_c);
12593         }
12594         std::memcpy((void*)new_data,(void*)values,siz*sizeof(T));
12595         delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
12596       }
12597       return *this;
12598     }
12599 
12600     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
12601     template<typename t>
12602     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
12603                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
12604       if (is_shared)
12605         throw CImgArgumentException(_cimg_instance
12606                                     "assign(): Invalid assignment request of shared instance from (%s*) buffer"
12607                                     "(pixel types are different).",
12608                                     cimg_instance,
12609                                     CImg<t>::pixel_type());
12610       return assign(values,size_x,size_y,size_z,size_c);
12611     }
12612 
12613     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
12614     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
12615                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
12616       const size_t siz = safe_size(size_x,size_y,size_z,size_c);
12617       if (!values || !siz) return assign();
12618       if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
12619       else {
12620         if (!_is_shared) {
12621           if (values + siz<_data || values>=_data + size()) assign();
12622           else cimg::warn(_cimg_instance
12623                           "assign(): Shared image instance has overlapping memory.",
12624                           cimg_instance);
12625         }
12626         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
12627         _data = const_cast<T*>(values);
12628       }
12629       return *this;
12630     }
12631 
12632     //! Construct image from memory buffer with specified size and pixel ordering scheme.
12633     template<typename t>
12634     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
12635                     const unsigned int size_z, const unsigned int size_c,
12636                     const char *const axes_order) {
12637       CImg<T>(values,size_x,size_y,size_z,size_c,axes_order).move_to(*this);
12638     }
12639 
12640     //! Construct image from reading an image file \inplace.
12641     /**
12642        In-place version of the constructor CImg(const char*).
12643     **/
12644     CImg<T>& assign(const char *const filename) {
12645       return load(filename);
12646     }
12647 
12648     //! Construct image copy \inplace.
12649     /**
12650        In-place version of the constructor CImg(const CImg<t>&).
12651     **/
12652     template<typename t>
12653     CImg<T>& assign(const CImg<t>& img) {
12654       return assign(img._data,img._width,img._height,img._depth,img._spectrum);
12655     }
12656 
12657     //! In-place version of the advanced copy constructor.
12658     /**
12659        In-place version of the constructor CImg(const CImg<t>&,bool).
12660      **/
12661     template<typename t>
12662     CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
12663       return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
12664     }
12665 
12666     //! Construct image with dimensions borrowed from another image \inplace.
12667     /**
12668        In-place version of the constructor CImg(const CImg<t>&,const char*).
12669     **/
12670     template<typename t>
12671     CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
12672       if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
12673       unsigned int siz[4] = { 0,1,1,1 }, k = 0;
12674       CImg<charT> item(256);
12675       for (const char *s = dimensions; *s && k<4; ++k) {
12676         if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item);
12677         if (*s) {
12678           unsigned int val = 0; char sep = 0;
12679           if (cimg_sscanf(s,"%u%c",&val,&sep)>0) {
12680             if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
12681             else siz[k] = val;
12682             while (*s>='0' && *s<='9') ++s;
12683             if (sep=='%') ++s;
12684           } else switch (cimg::lowercase(*s)) {
12685           case 'x' : case 'w' : siz[k] = img._width; ++s; break;
12686           case 'y' : case 'h' : siz[k] = img._height; ++s; break;
12687           case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
12688           case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
12689           default :
12690             throw CImgArgumentException(_cimg_instance
12691                                         "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
12692                                         cimg_instance,
12693                                         *s,dimensions);
12694           }
12695         }
12696       }
12697       return assign(siz[0],siz[1],siz[2],siz[3]);
12698     }
12699 
12700     //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace.
12701     /**
12702        In-place version of the constructor CImg(const CImg<t>&,const char*,T).
12703     **/
12704     template<typename t>
12705     CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T& value) {
12706       return assign(img,dimensions).fill(value);
12707     }
12708 
12709     //! Construct image from a display window \inplace.
12710     /**
12711        In-place version of the constructor CImg(const CImgDisplay&).
12712     **/
12713     CImg<T>& assign(const CImgDisplay &disp) {
12714       disp.snapshot(*this);
12715       return *this;
12716     }
12717 
12718     //! Construct empty image \inplace.
12719     /**
12720        Equivalent to assign().
12721        \note
12722        - It has been defined for compatibility with STL naming conventions.
12723     **/
12724     CImg<T>& clear() {
12725       return assign();
12726     }
12727 
12728     //! Transfer content of an image instance into another one.
12729     /**
12730        Transfer the dimensions and the pixel buffer content of an image instance into another one,
12731        and replace instance by an empty image. It avoids the copy of the pixel buffer
12732        when possible.
12733        \param img Destination image.
12734        \note
12735        - Pixel types \c T and \c t of source and destination images can be different, though the process is
12736          designed to be instantaneous when \c T and \c t are the same.
12737        \par Example
12738        \code
12739        CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'
12740                    dest(16,16);        // Construct a 16x16x1x1 (scalar) image
12741        src.move_to(dest);              // Now, 'src' is empty and 'dest' is the 256x256x1x3 image
12742        \endcode
12743     **/
12744     template<typename t>
12745     CImg<t>& move_to(CImg<t>& img) {
12746       img.assign(*this);
12747       assign();
12748       return img;
12749     }
12750 
12751     //! Transfer content of an image instance into another one \specialization.
12752     CImg<T>& move_to(CImg<T>& img) {
12753       if (_is_shared || img._is_shared) img.assign(*this);
12754       else swap(img);
12755       assign();
12756       return img;
12757     }
12758 
12759     //! Transfer content of an image instance into a new image in an image list.
12760     /**
12761        Transfer the dimensions and the pixel buffer content of an image instance
12762        into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
12763        \param list Destination list.
12764        \param pos Position of the newly inserted image in the list.
12765        \note
12766        - When optional parameter \c pos is omitted, the image instance is transferred as a new
12767          image at the end of the specified \c list.
12768        - It is convenient to sequentially insert new images into image lists, with no
12769          additional copies of memory buffer.
12770        \par Example
12771        \code
12772        CImgList<float> list;             // Construct an empty image list
12773        CImg<float> img("reference.jpg"); // Read image from filename
12774        img.move_to(list);                // Transfer image content as a new item in the list (no buffer copy)
12775        \endcode
12776     **/
12777     template<typename t>
12778     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
12779       const unsigned int npos = pos>list._width?list._width:pos;
12780       move_to(list.insert(1,npos)[npos]);
12781       return list;
12782     }
12783 
12784     //! Swap fields of two image instances.
12785     /**
12786       \param img Image to swap fields with.
12787       \note
12788       - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
12789         with algorithms requiring two swapping buffers.
12790       \par Example
12791       \code
12792       CImg<float> img1("lena.jpg"),
12793                   img2("milla.jpg");
12794       img1.swap(img2);    // Now, 'img1' is 'milla' and 'img2' is 'lena'
12795       \endcode
12796     **/
12797     CImg<T>& swap(CImg<T>& img) {
12798       cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum);
12799       cimg::swap(_data,img._data);
12800       cimg::swap(_is_shared,img._is_shared);
12801       return img;
12802     }
12803 
12804     //! Return a reference to an empty image.
12805     /**
12806        \note
12807        This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes,
12808        e.g.
12809        \code
12810        void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
12811        \endcode
12812      **/
12813     static CImg<T>& empty() {
12814       static CImg<T> _empty;
12815       return _empty.assign();
12816     }
12817 
12818     //! Return a reference to an empty image \const.
12819     static const CImg<T>& const_empty() {
12820       static const CImg<T> _empty;
12821       return _empty;
12822     }
12823 
12824     //@}
12825     //------------------------------------------
12826     //
12827     //! \name Overloaded Operators
12828     //@{
12829     //------------------------------------------
12830 
12831     //! Access to a pixel value.
12832     /**
12833        Return a reference to a located pixel value of the image instance,
12834        being possibly \e const, whether the image instance is \e const or not.
12835        This is the standard method to get/set pixel values in \c CImg<T> images.
12836        \param x X-coordinate of the pixel value.
12837        \param y Y-coordinate of the pixel value.
12838        \param z Z-coordinate of the pixel value.
12839        \param c C-coordinate of the pixel value.
12840        \note
12841        - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to
12842          <tt>(width() - 1,height() - 1,depth() - 1,spectrum() - 1)</tt>.
12843        - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the
12844          corresponding dimension is equal to \c 1.
12845          For instance, pixels of a 2D image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of
12846          <tt>img(x,y,0,c)</tt>.
12847        \warning
12848        - There is \e no boundary checking done in this operator, to make it as fast as possible.
12849          You \e must take care of out-of-bounds access by yourself, if necessary.
12850          For debugging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary
12851          checking operations in this operator. In that case, warning messages will be printed on the error output
12852          when accessing out-of-bounds pixels.
12853        \par Example
12854        \code
12855        CImg<float> img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'
12856        const float
12857           valR = img(10,10,0,0), // Read red value at coordinates (10,10)
12858           valG = img(10,10,0,1), // Read green value at coordinates (10,10)
12859           valB = img(10,10,2),   // Read blue value at coordinates (10,10) (Z-coordinate can be omitted)
12860           avg = (valR + valG + valB)/3; // Compute average pixel value
12861        img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value
12862        \endcode
12863     **/
12864 #if cimg_verbosity>=3
12865     T& operator()(const unsigned int x, const unsigned int y=0,
12866                   const unsigned int z=0, const unsigned int c=0) {
12867       const ulongT off = (ulongT)offset(x,y,z,c);
12868       if (!_data || off>=size()) {
12869         cimg::warn(_cimg_instance
12870                    "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].",
12871                    cimg_instance,
12872                    (int)x,(int)y,(int)z,(int)c,off);
12873         return *_data;
12874       }
12875       else return _data[off];
12876     }
12877 
12878     //! Access to a pixel value \const.
12879     const T& operator()(const unsigned int x, const unsigned int y=0,
12880                         const unsigned int z=0, const unsigned int c=0) const {
12881       return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
12882     }
12883 
12884     //! Access to a pixel value.
12885     /**
12886        \param x X-coordinate of the pixel value.
12887        \param y Y-coordinate of the pixel value.
12888        \param z Z-coordinate of the pixel value.
12889        \param c C-coordinate of the pixel value.
12890        \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
12891        \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
12892        \note
12893        - Similar to (but faster than) operator()().
12894          It uses precomputed offsets to optimize memory access. You may use it to optimize
12895          the reading/writing of several pixel values in the same image (e.g. in a loop).
12896      **/
12897     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
12898                   const ulongT wh, const ulongT whd=0) {
12899       cimg::unused(wh,whd);
12900       return (*this)(x,y,z,c);
12901     }
12902 
12903     //! Access to a pixel value \const.
12904     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
12905                         const ulongT wh, const ulongT whd=0) const {
12906       cimg::unused(wh,whd);
12907       return (*this)(x,y,z,c);
12908     }
12909 #else
12910     T& operator()(const unsigned int x) {
12911       return _data[x];
12912     }
12913 
12914     const T& operator()(const unsigned int x) const {
12915       return _data[x];
12916     }
12917 
12918     T& operator()(const unsigned int x, const unsigned int y) {
12919       return _data[x + y*_width];
12920     }
12921 
12922     const T& operator()(const unsigned int x, const unsigned int y) const {
12923       return _data[x + y*_width];
12924     }
12925 
12926     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
12927       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
12928    }
12929 
12930     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
12931       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
12932     }
12933 
12934     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
12935       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
12936     }
12937 
12938     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
12939       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
12940     }
12941 
12942     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
12943                   const ulongT wh) {
12944       return _data[x + y*_width + z*wh];
12945     }
12946 
12947     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
12948                         const ulongT wh) const {
12949       return _data[x + y*_width + z*wh];
12950     }
12951 
12952     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
12953                   const ulongT wh, const ulongT whd) {
12954       return _data[x + y*_width + z*wh + c*whd];
12955     }
12956 
12957     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
12958                         const ulongT wh, const ulongT whd) const {
12959       return _data[x + y*_width + z*wh + c*whd];
12960     }
12961 #endif
12962 
12963     //! Implicitly cast an image into a \c T*.
12964     /**
12965        Implicitly cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
12966        is \e const or not. The returned pointer points on the first value of the image pixel buffer.
12967        \note
12968        - It simply returns the pointer data() to the pixel buffer.
12969        - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
12970        \code
12971        CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image
12972        if (img1) {                      // Test succeeds, 'img1' is not an empty image
12973          if (!img2) {                   // Test succeeds, 'img2' is an empty image
12974            std::printf("'img1' is not empty, 'img2' is empty.");
12975          }
12976        }
12977        \endcode
12978        - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
12979        \code
12980        CImg<float> img(100,100);
12981        const float value = img[99]; // Access to value of the last pixel on the first row
12982        img[510] = 255;              // Set pixel value at (10,5)
12983        \endcode
12984     **/
12985     operator T*() {
12986       return _data;
12987     }
12988 
12989     //! Implicitly cast an image into a \c T* \const.
12990     operator const T*() const {
12991       return _data;
12992     }
12993 
12994     //! Assign a value to all image pixels.
12995     /**
12996        Assign specified \c value to each pixel value of the image instance.
12997        \param value Value that will be assigned to image pixels.
12998        \note
12999        - The image size is never modified.
13000        - The \c value may be casted to pixel type \c T if necessary.
13001        \par Example
13002        \code
13003        CImg<char> img(100,100); // Declare image (with garbage values)
13004        img = 0;                 // Set all pixel values to '0'
13005        img = 1.2;               // Set all pixel values to '1' (cast of '1.2' as a 'char')
13006        \endcode
13007     **/
13008     CImg<T>& operator=(const T& value) {
13009       return fill(value);
13010     }
13011 
13012     //! Assign pixels values from a specified expression.
13013     /**
13014        Initialize all pixel values from the specified string \c expression.
13015        \param expression Value string describing the way pixel values are set.
13016        \note
13017        - String parameter \c expression may describe different things:
13018          - 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"),
13019            the pixel values are set from specified \c expression and the image size is not modified.
13020          - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and
13021            replace the image instance. The image size is modified if necessary.
13022        \par Example
13023        \code
13024        CImg<float> img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with uninitialized values
13025        img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence
13026        img2 = "10*((x*y)%25)";                       // Set pixel values of 'img2' from a formula
13027        img3 = "reference.jpg";                       // Set pixel values of 'img3' from a file (image size is modified)
13028        (img1,img2,img3).display();
13029        \endcode
13030        \image html ref_operator_eq.jpg
13031     **/
13032     CImg<T>& operator=(const char *const expression) {
13033       const unsigned int omode = cimg::exception_mode();
13034       cimg::exception_mode(0);
13035       try {
13036         _fill(expression,true,1,0,0,"operator=",0);
13037       } catch (CImgException&) {
13038         cimg::exception_mode(omode);
13039         load(expression);
13040       }
13041       cimg::exception_mode(omode);
13042       return *this;
13043     }
13044 
13045     //! Copy an image into the current image instance.
13046     /**
13047        Similar to the in-place copy constructor assign(const CImg<t>&).
13048     **/
13049     template<typename t>
13050     CImg<T>& operator=(const CImg<t>& img) {
13051       return assign(img);
13052     }
13053 
13054     //! Copy an image into the current image instance \specialization.
13055     CImg<T>& operator=(const CImg<T>& img) {
13056       return assign(img);
13057     }
13058 
13059     //! Copy the content of a display window to the current image instance.
13060     /**
13061        Similar to assign(const CImgDisplay&).
13062     **/
13063     CImg<T>& operator=(const CImgDisplay& disp) {
13064       disp.snapshot(*this);
13065       return *this;
13066     }
13067 
13068     //! In-place addition operator.
13069     /**
13070        Add specified \c value to all pixels of an image instance.
13071        \param value Value to add.
13072        \note
13073        - Resulting pixel values are casted to fit the pixel type \c T.
13074          For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed.
13075        - Overflow values are treated as with standard C++ numeric types. For instance,
13076        \code
13077        CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'
13078        img+=1;                                   // Add '1' to each pixels -> Overflow
13079        // here all pixels of image 'img' are equal to '0'.
13080        \endcode
13081        - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double,
13082          and use cut() after addition.
13083        \par Example
13084        \code
13085        CImg<unsigned char> img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255])
13086        CImg<float> img2(img1); // Construct a float-valued copy of 'img1'
13087        img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats
13088        img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint
13089        img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1'
13090        const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way
13091        (img1,img2,img3).display();
13092        \endcode
13093        \image html ref_operator_plus.jpg
13094      **/
13095     template<typename t>
13096     CImg<T>& operator+=(const t value) {
13097       if (is_empty()) return *this;
13098       cimg_openmp_for(*this,*ptr + value,524288);
13099       return *this;
13100     }
13101 
13102     //! In-place addition operator.
13103     /**
13104        Add values to image pixels, according to the specified string \c expression.
13105        \param expression Value string describing the way pixel values are added.
13106        \note
13107        - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
13108          instead of assigning them.
13109     **/
13110     CImg<T>& operator+=(const char *const expression) {
13111       return *this+=(+*this)._fill(expression,true,1,0,0,"operator+=",this);
13112     }
13113 
13114     //! In-place addition operator.
13115     /**
13116        Add values to image pixels, according to the values of the input image \c img.
13117        \param img Input image to add.
13118        \note
13119        - The size of the image instance is never modified.
13120        - It is not mandatory that input image \c img has the same size as the image instance.
13121          If less values are available in \c img, then the values are added periodically. For instance, adding one
13122          WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3)
13123          means each color channel will be incremented with the same values at the same locations.
13124        \par Example
13125        \code
13126        CImg<float> img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3)
13127        // Construct a scalar shading (img2.spectrum()==1).
13128        const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2");
13129        img1+=img2; // Add shading to each channel of 'img1'
13130        img1.cut(0,255); // Prevent [0,255] overflow
13131        (img2,img1).display();
13132        \endcode
13133        \image html ref_operator_plus1.jpg
13134     **/
13135     template<typename t>
13136     CImg<T>& operator+=(const CImg<t>& img) {
13137       const ulongT siz = size(), isiz = img.size();
13138       if (siz && isiz) {
13139         if (is_overlapped(img)) return *this+=+img;
13140         T *ptrd = _data, *const ptre = _data + siz;
13141         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13142           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13143             *ptrd = (T)(*ptrd + *(ptrs++));
13144         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
13145       }
13146       return *this;
13147     }
13148 
13149     //! In-place increment operator (prefix).
13150     /**
13151        Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
13152        \note
13153        - Writing \c ++img is equivalent to \c img+=1.
13154      **/
13155     CImg<T>& operator++() {
13156       if (is_empty()) return *this;
13157       cimg_openmp_for(*this,*ptr + 1,524288);
13158       return *this;
13159     }
13160 
13161     //! In-place increment operator (postfix).
13162     /**
13163        Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
13164        \note
13165        - Use the prefixed version operator++() if you don't need a copy of the initial
13166          (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage.
13167      **/
13168     CImg<T> operator++(int) {
13169       const CImg<T> copy(*this,false);
13170       ++*this;
13171       return copy;
13172     }
13173 
13174     //! Return a non-shared copy of the image instance.
13175     /**
13176        \note
13177        - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
13178          Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image,
13179          and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no
13180          information about the shared state of the input image.
13181        - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
13182     **/
13183     CImg<T> operator+() const {
13184       return CImg<T>(*this,false);
13185     }
13186 
13187     //! Addition operator.
13188     /**
13189        Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
13190        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13191      **/
13192     template<typename t>
13193     CImg<_cimg_Tt> operator+(const t value) const {
13194       return CImg<_cimg_Tt>(*this,false)+=value;
13195     }
13196 
13197     //! Addition operator.
13198     /**
13199        Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
13200        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13201      **/
13202     CImg<Tfloat> operator+(const char *const expression) const {
13203       return CImg<Tfloat>(*this,false)+=expression;
13204     }
13205 
13206     //! Addition operator.
13207     /**
13208        Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13209        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13210      **/
13211     template<typename t>
13212     CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
13213       return CImg<_cimg_Tt>(*this,false)+=img;
13214     }
13215 
13216     //! In-place subtraction operator.
13217     /**
13218        Similar to operator+=(const t), except that it performs a subtraction instead of an addition.
13219      **/
13220     template<typename t>
13221     CImg<T>& operator-=(const t value) {
13222       if (is_empty()) return *this;
13223       cimg_openmp_for(*this,*ptr - value,524288);
13224       return *this;
13225     }
13226 
13227     //! In-place subtraction operator.
13228     /**
13229        Similar to operator+=(const char*), except that it performs a subtraction instead of an addition.
13230      **/
13231     CImg<T>& operator-=(const char *const expression) {
13232       return *this-=(+*this)._fill(expression,true,1,0,0,"operator-=",this);
13233     }
13234 
13235     //! In-place subtraction operator.
13236     /**
13237        Similar to operator+=(const CImg<t>&), except that it performs a subtraction instead of an addition.
13238      **/
13239     template<typename t>
13240     CImg<T>& operator-=(const CImg<t>& img) {
13241       const ulongT siz = size(), isiz = img.size();
13242       if (siz && isiz) {
13243         if (is_overlapped(img)) return *this-=+img;
13244         T *ptrd = _data, *const ptre = _data + siz;
13245         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13246           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13247             *ptrd = (T)(*ptrd - *(ptrs++));
13248         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
13249       }
13250       return *this;
13251     }
13252 
13253     //! In-place decrement operator (prefix).
13254     /**
13255        Similar to operator++(), except that it performs a decrement instead of an increment.
13256     **/
13257     CImg<T>& operator--() {
13258       if (is_empty()) return *this;
13259       cimg_openmp_for(*this,*ptr - 1,524288);
13260       return *this;
13261     }
13262 
13263     //! In-place decrement operator (postfix).
13264     /**
13265        Similar to operator++(int), except that it performs a decrement instead of an increment.
13266     **/
13267     CImg<T> operator--(int) {
13268       const CImg<T> copy(*this,false);
13269       --*this;
13270       return copy;
13271     }
13272 
13273     //! Replace each pixel by its opposite value.
13274     /**
13275        \note
13276        - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types.
13277          For instance, the \c unsigned \c char opposite of \c 1 is \c 255.
13278        \par Example
13279        \code
13280        const CImg<unsigned char>
13281          img1("reference.jpg"),   // Load a RGB color image
13282          img2 = -img1;            // Compute its opposite (in 'unsigned char')
13283        (img1,img2).display();
13284        \endcode
13285        \image html ref_operator_minus.jpg
13286      **/
13287     CImg<T> operator-() const {
13288       return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
13289     }
13290 
13291     //! Subtraction operator.
13292     /**
13293        Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
13294        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13295     **/
13296     template<typename t>
13297     CImg<_cimg_Tt> operator-(const t value) const {
13298       return CImg<_cimg_Tt>(*this,false)-=value;
13299     }
13300 
13301     //! Subtraction operator.
13302     /**
13303        Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
13304        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13305     **/
13306     CImg<Tfloat> operator-(const char *const expression) const {
13307       return CImg<Tfloat>(*this,false)-=expression;
13308     }
13309 
13310     //! Subtraction operator.
13311     /**
13312        Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13313        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13314     **/
13315     template<typename t>
13316     CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
13317       return CImg<_cimg_Tt>(*this,false)-=img;
13318     }
13319 
13320     //! In-place multiplication operator.
13321     /**
13322        Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
13323      **/
13324     template<typename t>
13325     CImg<T>& operator*=(const t value) {
13326       if (is_empty()) return *this;
13327       cimg_openmp_for(*this,*ptr * value,262144);
13328       return *this;
13329     }
13330 
13331     //! In-place multiplication operator.
13332     /**
13333        Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
13334      **/
13335     CImg<T>& operator*=(const char *const expression) {
13336       return mul((+*this)._fill(expression,true,1,0,0,"operator*=",this));
13337     }
13338 
13339     //! In-place multiplication operator.
13340     /**
13341        Replace the image instance by the matrix multiplication between the image instance and the specified matrix
13342        \c img.
13343        \param img Second operand of the matrix multiplication.
13344        \note
13345        - It does \e not compute a pointwise multiplication between two images. For this purpose, use
13346          mul(const CImg<t>&) instead.
13347        - The size of the image instance can be modified by this operator.
13348        \par Example
13349        \code
13350        CImg<float> A(2,2,1,1, 1,2,3,4);   // Construct 2x2 matrix A = [1,2;3,4]
13351        const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2]
13352        A*=X;                              // Assign matrix multiplication A*X to 'A'
13353        // 'A' is now a 1x2 vector whose values are [5;11].
13354        \endcode
13355     **/
13356     template<typename t>
13357     CImg<T>& operator*=(const CImg<t>& img) {
13358       return ((*this)*img).move_to(*this);
13359     }
13360 
13361     //! Multiplication operator.
13362     /**
13363        Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
13364        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13365     **/
13366     template<typename t>
13367     CImg<_cimg_Tt> operator*(const t value) const {
13368       return CImg<_cimg_Tt>(*this,false)*=value;
13369     }
13370 
13371     //! Multiplication operator.
13372     /**
13373        Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
13374        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13375     **/
13376     CImg<Tfloat> operator*(const char *const expression) const {
13377       return CImg<Tfloat>(*this,false)*=expression;
13378     }
13379 
13380     //! Multiplication operator.
13381     /**
13382        Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13383        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13384     **/
13385     template<typename t>
13386     CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
13387       typedef _cimg_Ttdouble Ttdouble;
13388       typedef _cimg_Tt Tt;
13389       if (_width!=img._height || _depth!=1 || _spectrum!=1)
13390         throw CImgArgumentException(_cimg_instance
13391                                     "operator*(): Invalid multiplication of instance by specified "
13392                                     "matrix (%u,%u,%u,%u,%p).",
13393                                     cimg_instance,
13394                                     img._width,img._height,img._depth,img._spectrum,img._data);
13395       CImg<Tt> res(img._width,_height);
13396 
13397       // Check for common cases to optimize.
13398       if (img._width==1) { // Matrix * Vector
13399         if (_height==1) switch (_width) { // Vector^T * Vector
13400           case 1 :
13401             res[0] = (Tt)((Ttdouble)_data[0]*img[0]);
13402             return res;
13403           case 2 :
13404             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]);
13405             return res;
13406           case 3 :
13407             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
13408                           (Ttdouble)_data[2]*img[2]);
13409             return res;
13410           case 4 :
13411             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
13412                           (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]);
13413             return res;
13414           default : {
13415             Ttdouble val = 0;
13416             cimg_pragma_openmp(parallel for reduction(+:val) cimg_openmp_if_size(size(),4096))
13417             cimg_forX(*this,i) val+=(Ttdouble)_data[i]*img[i];
13418             res[0] = val;
13419             return res;
13420           }
13421           } else if (_height==_width) switch (_width) { // Square_matrix * Vector
13422           case 2 : // 2x2_matrix * Vector
13423             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]);
13424             res[1] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[1]);
13425             return res;
13426           case 3 : // 3x3_matrix * Vector
13427             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
13428                           (Ttdouble)_data[2]*img[2]);
13429             res[1] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[1] +
13430                           (Ttdouble)_data[5]*img[2]);
13431             res[2] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[1] +
13432                           (Ttdouble)_data[8]*img[2]);
13433             return res;
13434           case 4 : // 4x4_matrix * Vector
13435             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
13436                           (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]);
13437             res[1] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[1] +
13438                           (Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[3]);
13439             res[2] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[1] +
13440                           (Ttdouble)_data[10]*img[2] + (Ttdouble)_data[11]*img[3]);
13441             res[3] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[1] +
13442                           (Ttdouble)_data[14]*img[2] + (Ttdouble)_data[15]*img[3]);
13443             return res;
13444           }
13445       } else if (_height==_width) {
13446         if (img._height==img._width) switch (_width) { // Square_matrix * Square_matrix
13447           case 2 : // 2x2_matrix * 2x2_matrix
13448             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[2]);
13449             res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[3]);
13450             res[2] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[2]);
13451             res[3] = (Tt)((Ttdouble)_data[2]*img[1] + (Ttdouble)_data[3]*img[3]);
13452             return res;
13453           case 3 : // 3x3_matrix * 3x3_matrix
13454             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[3] +
13455                           (Ttdouble)_data[2]*img[6]);
13456             res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[4] +
13457                           (Ttdouble)_data[2]*img[7]);
13458             res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[5] +
13459                           (Ttdouble)_data[2]*img[8]);
13460             res[3] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[3] +
13461                           (Ttdouble)_data[5]*img[6]);
13462             res[4] = (Tt)((Ttdouble)_data[3]*img[1] + (Ttdouble)_data[4]*img[4] +
13463                           (Ttdouble)_data[5]*img[7]);
13464             res[5] = (Tt)((Ttdouble)_data[3]*img[2] + (Ttdouble)_data[4]*img[5] +
13465                           (Ttdouble)_data[5]*img[8]);
13466             res[6] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[3] +
13467                           (Ttdouble)_data[8]*img[6]);
13468             res[7] = (Tt)((Ttdouble)_data[6]*img[1] + (Ttdouble)_data[7]*img[4] +
13469                           (Ttdouble)_data[8]*img[7]);
13470             res[8] = (Tt)((Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[5] +
13471                           (Ttdouble)_data[8]*img[8]);
13472             return res;
13473           case 4 : // 4x4_matrix * 4x4_matrix
13474             res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[4] +
13475                           (Ttdouble)_data[2]*img[8] + (Ttdouble)_data[3]*img[12]);
13476             res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[5] +
13477                           (Ttdouble)_data[2]*img[9] + (Ttdouble)_data[3]*img[13]);
13478             res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[6] +
13479                           (Ttdouble)_data[2]*img[10] + (Ttdouble)_data[3]*img[14]);
13480             res[3] = (Tt)((Ttdouble)_data[0]*img[3] + (Ttdouble)_data[1]*img[7] +
13481                           (Ttdouble)_data[2]*img[11] + (Ttdouble)_data[3]*img[15]);
13482             res[4] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[4] +
13483                           (Ttdouble)_data[6]*img[8] + (Ttdouble)_data[7]*img[12]);
13484             res[5] = (Tt)((Ttdouble)_data[4]*img[1] + (Ttdouble)_data[5]*img[5] +
13485                           (Ttdouble)_data[6]*img[9] + (Ttdouble)_data[7]*img[13]);
13486             res[6] = (Tt)((Ttdouble)_data[4]*img[2] + (Ttdouble)_data[5]*img[6] +
13487                           (Ttdouble)_data[6]*img[10] + (Ttdouble)_data[7]*img[14]);
13488             res[7] = (Tt)((Ttdouble)_data[4]*img[3] + (Ttdouble)_data[5]*img[7] +
13489                           (Ttdouble)_data[6]*img[11] + (Ttdouble)_data[7]*img[15]);
13490             res[8] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[4] +
13491                           (Ttdouble)_data[10]*img[8] + (Ttdouble)_data[11]*img[12]);
13492             res[9] = (Tt)((Ttdouble)_data[8]*img[1] + (Ttdouble)_data[9]*img[5] +
13493                           (Ttdouble)_data[10]*img[9] + (Ttdouble)_data[11]*img[13]);
13494             res[10] = (Tt)((Ttdouble)_data[8]*img[2] + (Ttdouble)_data[9]*img[6] +
13495                            (Ttdouble)_data[10]*img[10] + (Ttdouble)_data[11]*img[14]);
13496             res[11] = (Tt)((Ttdouble)_data[8]*img[3] + (Ttdouble)_data[9]*img[7] +
13497                            (Ttdouble)_data[10]*img[11] + (Ttdouble)_data[11]*img[15]);
13498             res[12] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[4] +
13499                            (Ttdouble)_data[14]*img[8] + (Ttdouble)_data[15]*img[12]);
13500             res[13] = (Tt)((Ttdouble)_data[12]*img[1] + (Ttdouble)_data[13]*img[5] +
13501                            (Ttdouble)_data[14]*img[9] + (Ttdouble)_data[15]*img[13]);
13502             res[14] = (Tt)((Ttdouble)_data[12]*img[2] + (Ttdouble)_data[13]*img[6] +
13503                            (Ttdouble)_data[14]*img[10] + (Ttdouble)_data[15]*img[14]);
13504             res[15] = (Tt)((Ttdouble)_data[12]*img[3] + (Ttdouble)_data[13]*img[7] +
13505                            (Ttdouble)_data[14]*img[11] + (Ttdouble)_data[15]*img[15]);
13506             return res;
13507           } else switch (_width) { // Square_matrix * Matrix
13508           case 2 : { // 2x2_matrix * Matrix
13509             const t *const ps0 = img.data(), *const ps1 = img.data(0,1);
13510             Tt *const pd0 = res.data(), *const pd1 = res.data(0,1);
13511             const Ttdouble
13512               a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1],
13513               a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3];
13514             cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),4096))
13515             cimg_forX(img,i) {
13516               const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i];
13517               pd0[i] = (Tt)(a0*x + a1*y);
13518               pd1[i] = (Tt)(a2*x + a3*y);
13519             }
13520             return res;
13521           }
13522           case 3 : { // 3x3_matrix * Matrix
13523             const t *const ps0 = img.data(), *const ps1 = img.data(0,1), *const ps2 = img.data(0,2);
13524             Tt *const pd0 = res.data(), *const pd1 = res.data(0,1), *const pd2 = res.data(0,2);
13525             const Ttdouble
13526               a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2],
13527               a3 = (Ttdouble)_data[3], a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5],
13528               a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], a8 = (Ttdouble)_data[8];
13529             cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),1024))
13530             cimg_forX(img,i) {
13531               const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i];
13532               pd0[i] = (Tt)(a0*x + a1*y + a2*z);
13533               pd1[i] = (Tt)(a3*x + a4*y + a5*z);
13534               pd2[i] = (Tt)(a6*x + a7*y + a8*z);
13535             }
13536             return res;
13537           }
13538           case 4 : { // 4x4_matrix * Matrix
13539             const t
13540               *const ps0 = img.data(), *const ps1 = img.data(0,1),
13541               *const ps2 = img.data(0,2), *const ps3 = img.data(0,3);
13542             Tt
13543               *const pd0 = res.data(), *const pd1 = res.data(0,1),
13544               *const pd2 = res.data(0,2), *const pd3 = res.data(0,3);
13545             const Ttdouble
13546               a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3],
13547               a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7],
13548               a8 = (Ttdouble)_data[8], a9 = (Ttdouble)_data[9], a10 = (Ttdouble)_data[10], a11 = (Ttdouble)_data[11],
13549               a12 = (Ttdouble)_data[12], a13 = (Ttdouble)_data[13], a14 = (Ttdouble)_data[14],
13550               a15 = (Ttdouble)_data[15];
13551             cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),512))
13552             cimg_forX(img,i) {
13553               const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i], c = (Ttdouble)ps3[i];
13554               pd0[i] = (Tt)(a0*x + a1*y + a2*z + a3*c);
13555               pd1[i] = (Tt)(a4*x + a5*y + a6*z + a7*c);
13556               pd2[i] = (Tt)(a8*x + a9*y + a10*z + a11*c);
13557               pd3[i] = (Tt)(a12*x + a13*y + a14*z + a15*c);
13558             }
13559             return res;
13560           }
13561           }
13562       }
13563 
13564       // Fallback to generic version.
13565 #if cimg_use_openmp!=0
13566       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
13567                          cimg_openmp_if(size()>(cimg_openmp_sizefactor)*1024 &&
13568                                         img.size()>(cimg_openmp_sizefactor)*1024))
13569         cimg_forXY(res,i,j) {
13570           Ttdouble value = 0;
13571           cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k);
13572           res(i,j) = (Tt)value;
13573       }
13574 #else
13575       Tt *ptrd = res._data;
13576       cimg_forXY(res,i,j) {
13577         Ttdouble value = 0;
13578         cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k);
13579         *(ptrd++) = (Tt)value;
13580       }
13581 #endif
13582       return res;
13583     }
13584 
13585     //! In-place division operator.
13586     /**
13587        Similar to operator+=(const t), except that it performs a division instead of an addition.
13588      **/
13589     template<typename t>
13590     CImg<T>& operator/=(const t value) {
13591       if (is_empty()) return *this;
13592       cimg_openmp_for(*this,*ptr / value,32768);
13593       return *this;
13594     }
13595 
13596     //! In-place division operator.
13597     /**
13598        Similar to operator+=(const char*), except that it performs a division instead of an addition.
13599      **/
13600     CImg<T>& operator/=(const char *const expression) {
13601       return div((+*this)._fill(expression,true,1,0,0,"operator/=",this));
13602     }
13603 
13604     //! In-place division operator.
13605     /**
13606        Replace the image instance by the (right) matrix division between the image instance and the specified
13607        matrix \c img.
13608        \param img Second operand of the matrix division.
13609        \note
13610        - It does \e not compute a pointwise division between two images. For this purpose, use
13611          div(const CImg<t>&) instead.
13612        - It returns the matrix operation \c A*inverse(img).
13613        - The size of the image instance can be modified by this operator.
13614      **/
13615     template<typename t>
13616     CImg<T>& operator/=(const CImg<t>& img) {
13617       return (*this*img.get_invert()).move_to(*this);
13618     }
13619 
13620     //! Division operator.
13621     /**
13622        Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
13623        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13624     **/
13625     template<typename t>
13626     CImg<_cimg_Tt> operator/(const t value) const {
13627       return CImg<_cimg_Tt>(*this,false)/=value;
13628     }
13629 
13630     //! Division operator.
13631     /**
13632        Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
13633        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13634     **/
13635     CImg<Tfloat> operator/(const char *const expression) const {
13636       return CImg<Tfloat>(*this,false)/=expression;
13637     }
13638 
13639     //! Division operator.
13640     /**
13641        Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13642        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13643     **/
13644     template<typename t>
13645     CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
13646       return (*this)*img.get_invert();
13647     }
13648 
13649     //! In-place modulo operator.
13650     /**
13651        Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
13652     **/
13653     template<typename t>
13654     CImg<T>& operator%=(const t value) {
13655       if (is_empty()) return *this;
13656       cimg_openmp_for(*this,cimg::mod(*ptr,(T)value),16384);
13657       return *this;
13658     }
13659 
13660     //! In-place modulo operator.
13661     /**
13662        Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
13663     **/
13664     CImg<T>& operator%=(const char *const expression) {
13665       return *this%=(+*this)._fill(expression,true,1,0,0,"operator%=",this);
13666     }
13667 
13668     //! In-place modulo operator.
13669     /**
13670        Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
13671     **/
13672     template<typename t>
13673     CImg<T>& operator%=(const CImg<t>& img) {
13674       const ulongT siz = size(), isiz = img.size();
13675       if (siz && isiz) {
13676         if (is_overlapped(img)) return *this%=+img;
13677         T *ptrd = _data, *const ptre = _data + siz;
13678         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13679           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13680             *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
13681         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
13682       }
13683       return *this;
13684     }
13685 
13686     //! Modulo operator.
13687     /**
13688        Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
13689        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13690     **/
13691     template<typename t>
13692     CImg<_cimg_Tt> operator%(const t value) const {
13693       return CImg<_cimg_Tt>(*this,false)%=value;
13694     }
13695 
13696     //! Modulo operator.
13697     /**
13698        Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
13699        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13700     **/
13701     CImg<Tfloat> operator%(const char *const expression) const {
13702       return CImg<Tfloat>(*this,false)%=expression;
13703     }
13704 
13705     //! Modulo operator.
13706     /**
13707        Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13708        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
13709     **/
13710     template<typename t>
13711     CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
13712       return CImg<_cimg_Tt>(*this,false)%=img;
13713     }
13714 
13715     //! In-place bitwise AND operator.
13716     /**
13717        Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
13718     **/
13719     template<typename t>
13720     CImg<T>& operator&=(const t value) {
13721       if (is_empty()) return *this;
13722       cimg_openmp_for(*this,(ulongT)*ptr & (ulongT)value,32768);
13723       return *this;
13724     }
13725 
13726     //! In-place bitwise AND operator.
13727     /**
13728        Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
13729     **/
13730     CImg<T>& operator&=(const char *const expression) {
13731       return *this&=(+*this)._fill(expression,true,1,0,0,"operator&=",this);
13732     }
13733 
13734     //! In-place bitwise AND operator.
13735     /**
13736        Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
13737     **/
13738     template<typename t>
13739     CImg<T>& operator&=(const CImg<t>& img) {
13740       const ulongT siz = size(), isiz = img.size();
13741       if (siz && isiz) {
13742         if (is_overlapped(img)) return *this&=+img;
13743         T *ptrd = _data, *const ptre = _data + siz;
13744         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13745           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13746             *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
13747         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
13748       }
13749       return *this;
13750     }
13751 
13752     //! Bitwise AND operator.
13753     /**
13754        Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
13755        The pixel type of the returned image is \c T.
13756     **/
13757     template<typename t>
13758     CImg<T> operator&(const t value) const {
13759       return (+*this)&=value;
13760     }
13761 
13762     //! Bitwise AND operator.
13763     /**
13764        Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
13765        The pixel type of the returned image is \c T.
13766     **/
13767     CImg<T> operator&(const char *const expression) const {
13768       return (+*this)&=expression;
13769     }
13770 
13771     //! Bitwise AND operator.
13772     /**
13773        Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13774        The pixel type of the returned image is \c T.
13775     **/
13776     template<typename t>
13777     CImg<T> operator&(const CImg<t>& img) const {
13778       return (+*this)&=img;
13779     }
13780 
13781     //! In-place bitwise OR operator.
13782     /**
13783        Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
13784     **/
13785     template<typename t>
13786     CImg<T>& operator|=(const t value) {
13787       if (is_empty()) return *this;
13788       cimg_openmp_for(*this,(ulongT)*ptr | (ulongT)value,32768);
13789       return *this;
13790     }
13791 
13792     //! In-place bitwise OR operator.
13793     /**
13794        Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
13795     **/
13796     CImg<T>& operator|=(const char *const expression) {
13797       return *this|=(+*this)._fill(expression,true,1,0,0,"operator|=",this);
13798     }
13799 
13800     //! In-place bitwise OR operator.
13801     /**
13802        Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
13803     **/
13804     template<typename t>
13805     CImg<T>& operator|=(const CImg<t>& img) {
13806       const ulongT siz = size(), isiz = img.size();
13807       if (siz && isiz) {
13808         if (is_overlapped(img)) return *this|=+img;
13809         T *ptrd = _data, *const ptre = _data + siz;
13810         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13811           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13812             *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
13813         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
13814       }
13815       return *this;
13816     }
13817 
13818     //! Bitwise OR operator.
13819     /**
13820        Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
13821        The pixel type of the returned image is \c T.
13822     **/
13823     template<typename t>
13824     CImg<T> operator|(const t value) const {
13825       return (+*this)|=value;
13826     }
13827 
13828     //! Bitwise OR operator.
13829     /**
13830        Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
13831        The pixel type of the returned image is \c T.
13832     **/
13833     CImg<T> operator|(const char *const expression) const {
13834       return (+*this)|=expression;
13835     }
13836 
13837     //! Bitwise OR operator.
13838     /**
13839        Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13840        The pixel type of the returned image is \c T.
13841     **/
13842     template<typename t>
13843     CImg<T> operator|(const CImg<t>& img) const {
13844       return (+*this)|=img;
13845     }
13846 
13847     //! In-place bitwise XOR operator.
13848     /**
13849        Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition.
13850        \warning
13851        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead.
13852     **/
13853     template<typename t>
13854     CImg<T>& operator^=(const t value) {
13855       if (is_empty()) return *this;
13856       cimg_openmp_for(*this,(ulongT)*ptr ^ (ulongT)value,32768);
13857       return *this;
13858     }
13859 
13860     //! In-place bitwise XOR operator.
13861     /**
13862        Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
13863        \warning
13864        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
13865     **/
13866     CImg<T>& operator^=(const char *const expression) {
13867       return *this^=(+*this)._fill(expression,true,1,0,0,"operator^=",this);
13868     }
13869 
13870     //! In-place bitwise XOR operator.
13871     /**
13872        Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
13873        \warning
13874        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
13875     **/
13876     template<typename t>
13877     CImg<T>& operator^=(const CImg<t>& img) {
13878       const ulongT siz = size(), isiz = img.size();
13879       if (siz && isiz) {
13880         if (is_overlapped(img)) return *this^=+img;
13881         T *ptrd = _data, *const ptre = _data + siz;
13882         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13883           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13884             *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
13885         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
13886       }
13887       return *this;
13888     }
13889 
13890     //! Bitwise XOR operator.
13891     /**
13892        Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
13893        The pixel type of the returned image is \c T.
13894     **/
13895     template<typename t>
13896     CImg<T> operator^(const t value) const {
13897       return (+*this)^=value;
13898     }
13899 
13900     //! Bitwise XOR operator.
13901     /**
13902        Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
13903        The pixel type of the returned image is \c T.
13904     **/
13905     CImg<T> operator^(const char *const expression) const {
13906       return (+*this)^=expression;
13907     }
13908 
13909     //! Bitwise XOR operator.
13910     /**
13911        Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
13912        The pixel type of the returned image is \c T.
13913     **/
13914     template<typename t>
13915     CImg<T> operator^(const CImg<t>& img) const {
13916       return (+*this)^=img;
13917     }
13918 
13919     //! In-place bitwise left shift operator.
13920     /**
13921        Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
13922     **/
13923     template<typename t>
13924     CImg<T>& operator<<=(const t value) {
13925       if (is_empty()) return *this;
13926       cimg_openmp_for(*this,((longT)*ptr) << (int)value,65536);
13927       return *this;
13928     }
13929 
13930     //! In-place bitwise left shift operator.
13931     /**
13932        Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
13933     **/
13934     CImg<T>& operator<<=(const char *const expression) {
13935       return *this<<=(+*this)._fill(expression,true,1,0,0,"operator<<=",this);
13936     }
13937 
13938     //! In-place bitwise left shift operator.
13939     /**
13940        Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
13941     **/
13942     template<typename t>
13943     CImg<T>& operator<<=(const CImg<t>& img) {
13944       const ulongT siz = size(), isiz = img.size();
13945       if (siz && isiz) {
13946         if (is_overlapped(img)) return *this^=+img;
13947         T *ptrd = _data, *const ptre = _data + siz;
13948         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
13949           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
13950             *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
13951         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
13952       }
13953       return *this;
13954     }
13955 
13956     //! Bitwise left shift operator.
13957     /**
13958        Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
13959        The pixel type of the returned image is \c T.
13960     **/
13961     template<typename t>
13962     CImg<T> operator<<(const t value) const {
13963       return (+*this)<<=value;
13964     }
13965 
13966     //! Bitwise left shift operator.
13967     /**
13968        Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
13969        The pixel type of the returned image is \c T.
13970     **/
13971     CImg<T> operator<<(const char *const expression) const {
13972       return (+*this)<<=expression;
13973     }
13974 
13975     //! Bitwise left shift operator.
13976     /**
13977        Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of
13978        operating in-place.
13979        The pixel type of the returned image is \c T.
13980     **/
13981     template<typename t>
13982     CImg<T> operator<<(const CImg<t>& img) const {
13983       return (+*this)<<=img;
13984     }
13985 
13986     //! In-place bitwise right shift operator.
13987     /**
13988        Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
13989     **/
13990     template<typename t>
13991     CImg<T>& operator>>=(const t value) {
13992       if (is_empty()) return *this;
13993       cimg_openmp_for(*this,((longT)*ptr) >> (int)value,65536);
13994       return *this;
13995     }
13996 
13997     //! In-place bitwise right shift operator.
13998     /**
13999        Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
14000     **/
14001     CImg<T>& operator>>=(const char *const expression) {
14002       return *this>>=(+*this)._fill(expression,true,1,0,0,"operator>>=",this);
14003     }
14004 
14005     //! In-place bitwise right shift operator.
14006     /**
14007        Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
14008     **/
14009     template<typename t>
14010     CImg<T>& operator>>=(const CImg<t>& img) {
14011       const ulongT siz = size(), isiz = img.size();
14012       if (siz && isiz) {
14013         if (is_overlapped(img)) return *this^=+img;
14014         T *ptrd = _data, *const ptre = _data + siz;
14015         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
14016           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
14017             *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
14018         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
14019       }
14020       return *this;
14021     }
14022 
14023     //! Bitwise right shift operator.
14024     /**
14025        Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
14026        The pixel type of the returned image is \c T.
14027     **/
14028     template<typename t>
14029     CImg<T> operator>>(const t value) const {
14030       return (+*this)>>=value;
14031     }
14032 
14033     //! Bitwise right shift operator.
14034     /**
14035        Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
14036        The pixel type of the returned image is \c T.
14037     **/
14038     CImg<T> operator>>(const char *const expression) const {
14039       return (+*this)>>=expression;
14040     }
14041 
14042     //! Bitwise right shift operator.
14043     /**
14044        Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of
14045        operating in-place.
14046        The pixel type of the returned image is \c T.
14047     **/
14048     template<typename t>
14049     CImg<T> operator>>(const CImg<t>& img) const {
14050       return (+*this)>>=img;
14051     }
14052 
14053     //! Bitwise inversion operator.
14054     /**
14055        Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
14056     **/
14057     CImg<T> operator~() const {
14058       CImg<T> res(_width,_height,_depth,_spectrum);
14059       const T *ptrs = _data;
14060       cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; }
14061       return res;
14062     }
14063 
14064     //! Test if all pixels of an image have the same value.
14065     /**
14066        Return \c true is all pixels of the image instance are equal to the specified \c value.
14067        \param value Reference value to compare with.
14068     **/
14069     template<typename t>
14070     bool operator==(const t value) const {
14071       if (is_empty()) return false;
14072       typedef _cimg_Tt Tt;
14073       bool is_equal = true;
14074       for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
14075       return is_equal;
14076     }
14077 
14078     //! Test if all pixel values of an image follow a specified expression.
14079     /**
14080        Return \c true is all pixels of the image instance are equal to the specified \c expression.
14081        \param expression Value string describing the way pixel values are compared.
14082     **/
14083     bool operator==(const char *const expression) const {
14084       return *this==(+*this)._fill(expression,true,1,0,0,"operator==",this);
14085     }
14086 
14087     //! Test if two images have the same size and values.
14088     /**
14089        Return \c true if the image instance and the input image \c img have the same pixel values,
14090        even if the dimensions of the two images do not match. It returns \c false otherwise.
14091        \param img Input image to compare with.
14092        \note
14093        - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==()
14094          to return \c true.
14095          Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different
14096          pixel types \c T and \c t.
14097        \par Example
14098        \code
14099        const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values)
14100        const CImg<char> img2(1,3,1,1, 0,1,2);  // Construct a 1x3 vector [0;1;2] (with 'char' pixel values)
14101        if (img1==img2) {                       // Test succeeds, image dimensions and values are the same
14102          std::printf("'img1' and 'img2' have same dimensions and values.");
14103        }
14104        \endcode
14105     **/
14106     template<typename t>
14107     bool operator==(const CImg<t>& img) const {
14108       typedef _cimg_Tt Tt;
14109       const ulongT siz = size();
14110       bool is_equal = true;
14111       if (siz!=img.size()) return false;
14112       t *ptrs = img._data + siz;
14113       for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
14114       return is_equal;
14115     }
14116 
14117     //! Test if pixels of an image are all different from a value.
14118     /**
14119        Return \c true is all pixels of the image instance are different than the specified \c value.
14120        \param value Reference value to compare with.
14121     **/
14122     template<typename t>
14123     bool operator!=(const t value) const {
14124       return !((*this)==value);
14125     }
14126 
14127     //! Test if all pixel values of an image are different from a specified expression.
14128     /**
14129        Return \c true is all pixels of the image instance are different to the specified \c expression.
14130        \param expression Value string describing the way pixel values are compared.
14131     **/
14132     bool operator!=(const char *const expression) const {
14133       return !((*this)==expression);
14134     }
14135 
14136     //! Test if two images have different sizes or values.
14137     /**
14138        Return \c true if the image instance and the input image \c img have different dimensions or pixel values,
14139        and \c false otherwise.
14140        \param img Input image to compare with.
14141        \note
14142        - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
14143     **/
14144     template<typename t>
14145     bool operator!=(const CImg<t>& img) const {
14146       return !((*this)==img);
14147     }
14148 
14149     //! Construct an image list from two images.
14150     /**
14151        Return a new list of image (\c CImgList instance) containing exactly two elements:
14152          - A copy of the image instance, at position [\c 0].
14153          - A copy of the specified image \c img, at position [\c 1].
14154 
14155        \param img Input image that will be the second image of the resulting list.
14156        \note
14157        - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow
14158          in practice (see warning below).
14159        - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
14160          inserted as new non-shared copies in the resulting list.
14161        - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
14162        \warning
14163        - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
14164          This may become very expensive in terms of speed and used memory. You should avoid using this technique to
14165          build a new CImgList instance from several images, if you are seeking for performance.
14166          Fast insertions of images in an image list are possible with
14167          CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or move_to(CImgList<t>&,unsigned int).
14168        \par Example
14169        \code
14170        const CImg<float>
14171           img1("reference.jpg"),
14172           img2 = img1.get_mirror('x'),
14173           img3 = img2.get_blur(5);
14174        const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'
14175        (list,img3).display();                    // Display image list containing copies of 'img1','img2' and 'img3'
14176        \endcode
14177        \image html ref_operator_comma.jpg
14178     **/
14179     template<typename t>
14180     CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
14181       return CImgList<_cimg_Tt>(*this,img);
14182     }
14183 
14184     //! Construct an image list from image instance and an input image list.
14185     /**
14186        Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements:
14187          - A copy of the image instance, at position [\c 0].
14188          - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
14189 
14190        \param list Input image list that will be appended to the image instance.
14191        \note
14192        - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
14193     **/
14194     template<typename t>
14195     CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
14196       return CImgList<_cimg_Tt>(list,false).insert(*this,0);
14197     }
14198 
14199     //! Split image along specified axis.
14200     /**
14201        Return a new list of images (\c CImgList instance) containing the split components
14202        of the instance image along the specified axis.
14203        \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c')
14204        \note
14205        - Similar to get_split(char,int) const, with default second argument.
14206        \par Example
14207        \code
14208        const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image
14209        const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels
14210        (img,list).display();
14211        \endcode
14212        \image html ref_operator_less.jpg
14213     **/
14214     CImgList<T> operator<(const char axis) const {
14215       return get_split(axis);
14216     }
14217 
14218     //@}
14219     //-------------------------------------
14220     //
14221     //! \name Instance Characteristics
14222     //@{
14223     //-------------------------------------
14224 
14225     //! Return the type of image pixel values as a C string.
14226     /**
14227        Return a \c char* string containing the usual type name of the image pixel values
14228        (i.e. a stringified version of the template parameter \c T).
14229        \note
14230        - The returned string may contain spaces (as in \c "unsigned char").
14231        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
14232     **/
14233     static const char* pixel_type() {
14234       return cimg::type<T>::string();
14235     }
14236 
14237     //! Return the number of image columns.
14238     /**
14239        Return the image width, i.e. the image dimension along the X-axis.
14240        \note
14241        - The width() of an empty image is equal to \c 0.
14242        - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
14243        - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
14244          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
14245          \c unsigned \c int variables.
14246          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
14247          <tt>(*this)._width</tt>.
14248     **/
14249     int width() const {
14250       return (int)_width;
14251     }
14252 
14253     //! Return the number of image rows.
14254     /**
14255        Return the image height, i.e. the image dimension along the Y-axis.
14256        \note
14257        - The height() of an empty image is equal to \c 0.
14258        - height() returns an \c int, although the image height 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)._height</tt>.
14263     **/
14264     int height() const {
14265       return (int)_height;
14266     }
14267 
14268     //! Return the number of image slices.
14269     /**
14270        Return the image depth, i.e. the image dimension along the Z-axis.
14271        \note
14272        - The depth() of an empty image is equal to \c 0.
14273        - depth() is typically equal to \c 1 when considering usual 2D images. When depth()\c > \c 1, the image
14274          is said to be \e volumetric.
14275        - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
14276          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
14277          \c unsigned \c int variables.
14278          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
14279          <tt>(*this)._depth</tt>.
14280     **/
14281     int depth() const {
14282       return (int)_depth;
14283     }
14284 
14285     //! Return the number of image channels.
14286     /**
14287        Return the number of image channels, i.e. the image dimension along the C-axis.
14288        \note
14289        - The spectrum() of an empty image is equal to \c 0.
14290        - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3
14291          for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel).
14292          The number of channels of an image instance is not limited. The meaning of the pixel values is not linked
14293          up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
14294        - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
14295          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
14296          \c unsigned \c int variables.
14297          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
14298          <tt>(*this)._spectrum</tt>.
14299     **/
14300     int spectrum() const {
14301       return (int)_spectrum;
14302     }
14303 
14304     //! Return the total number of pixel values.
14305     /**
14306        Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
14307        i.e. the total number of values of type \c T in the pixel buffer of the image instance.
14308        \note
14309        - The size() of an empty image is equal to \c 0.
14310        - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to
14311          <tt>size()*sizeof(T)</tt>.
14312        \par Example
14313        \code
14314        const CImg<float> img(100,100,1,3);               // Construct new 100x100 color image
14315        if (img.size()==30000)                            // Test succeeds
14316          std::printf("Pixel buffer uses %lu bytes",
14317                      img.size()*sizeof(float));
14318        \endcode
14319     **/
14320     ulongT size() const {
14321       return (ulongT)_width*_height*_depth*_spectrum;
14322     }
14323 
14324     //! Return a pointer to the first pixel value.
14325     /**
14326        Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
14327        whether the instance is \c const or not.
14328        \note
14329        - The data() of an empty image is equal to \c 0 (null pointer).
14330        - The allocated pixel buffer for the image instance starts from \c data()
14331          and goes to <tt>data()+\ref size() - 1</tt> (included).
14332        - To get the pointer to one particular location of the pixel buffer, use
14333          data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
14334     **/
14335     T* data() {
14336       return _data;
14337     }
14338 
14339     //! Return a pointer to the first pixel value \const.
14340     const T* data() const {
14341       return _data;
14342     }
14343 
14344     //! Return a pointer to a located pixel value.
14345     /**
14346        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
14347        of the image instance,
14348        whether the instance is \c const or not.
14349        \param x X-coordinate of the pixel value.
14350        \param y Y-coordinate of the pixel value.
14351        \param z Z-coordinate of the pixel value.
14352        \param c C-coordinate of the pixel value.
14353        \note
14354        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same
14355          properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
14356      **/
14357 #if cimg_verbosity>=3
14358     T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
14359       const ulongT off = (ulongT)offset(x,y,z,c);
14360       if (off>=size())
14361         cimg::warn(_cimg_instance
14362                    "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
14363                    cimg_instance,
14364                    x,y,z,c,off);
14365       return _data + off;
14366     }
14367 
14368     //! Return a pointer to a located pixel value \const.
14369     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
14370       return const_cast<CImg<T>*>(this)->data(x,y,z,c);
14371     }
14372 #else
14373     T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
14374       return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
14375     }
14376 
14377     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
14378       return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
14379     }
14380 #endif
14381 
14382     //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer.
14383     /**
14384        \param x X-coordinate of the pixel value.
14385        \param y Y-coordinate of the pixel value.
14386        \param z Z-coordinate of the pixel value.
14387        \param c C-coordinate of the pixel value.
14388        \note
14389        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
14390          Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
14391        \par Example
14392        \code
14393        const CImg<float> img(100,100,1,3);      // Define a 100x100 RGB-color image
14394        const long off = img.offset(10,10,0,2);  // Get the offset of the blue value of the pixel located at (10,10)
14395        const float val = img[off];              // Get the blue value of this pixel
14396        \endcode
14397     **/
14398     longT offset(const int x, const int y=0, const int z=0, const int c=0) const {
14399       return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth;
14400     }
14401 
14402     //! Return a CImg<T>::iterator pointing to the first pixel value.
14403     /**
14404        \note
14405        - Equivalent to data().
14406        - It has been mainly defined for compatibility with STL naming conventions.
14407      **/
14408     iterator begin() {
14409       return _data;
14410     }
14411 
14412     //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
14413     const_iterator begin() const {
14414       return _data;
14415     }
14416 
14417     //! Return a CImg<T>::iterator pointing next to the last pixel value.
14418     /**
14419        \note
14420        - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
14421        - It has been mainly defined for compatibility with STL naming conventions.
14422        \warning
14423        - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer.
14424          Trying to read or write the content of the returned iterator will probably result in a crash.
14425          Use it mainly as a strict upper bound for a CImg<T>::iterator.
14426        \par Example
14427        \code
14428        CImg<float> img(100,100,1,3); // Define a 100x100 RGB color image
14429        // 'img.end()' used below as an upper bound for the iterator.
14430        for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)
14431          *it = 0;
14432        \endcode
14433     **/
14434     iterator end() {
14435       return _data + size();
14436     }
14437 
14438     //! Return a CImg<T>::iterator pointing next to the last pixel value \const.
14439     const_iterator end() const {
14440       return _data + size();
14441     }
14442 
14443     //! Return a reference to the first pixel value.
14444     /**
14445        \note
14446        - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
14447        - It has been mainly defined for compatibility with STL naming conventions.
14448     **/
14449     T& front() {
14450       return *_data;
14451     }
14452 
14453     //! Return a reference to the first pixel value \const.
14454     const T& front() const {
14455       return *_data;
14456     }
14457 
14458     //! Return a reference to the last pixel value.
14459     /**
14460        \note
14461        - Writing \c img.back() is equivalent to <tt>img[img.size() - 1]</tt>, or
14462          <tt>img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1)</tt>.
14463        - It has been mainly defined for compatibility with STL naming conventions.
14464     **/
14465     T& back() {
14466       return *(_data + size() - 1);
14467     }
14468 
14469     //! Return a reference to the last pixel value \const.
14470     const T& back() const {
14471       return *(_data + size() - 1);
14472     }
14473 
14474     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
14475     /**
14476        Return a reference to the pixel value of the image instance located at a specified \c offset,
14477        or to a specified default value in case of out-of-bounds access.
14478        \param offset Offset to the desired pixel value.
14479        \param out_value Default value returned if \c offset is outside image bounds.
14480        \note
14481        - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
14482          is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
14483          is safely returned instead.
14484        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
14485          you are \e not sure about the validity of the specified pixel offset.
14486     **/
14487     T& at(const int offset, const T& out_value) {
14488       return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
14489     }
14490 
14491     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
14492     T at(const int offset, const T& out_value) const {
14493       return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
14494     }
14495 
14496     //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
14497     /**
14498        Return a reference to the pixel value of the image instance located at a specified \c offset,
14499        or to the nearest pixel location in the image instance in case of out-of-bounds access.
14500        \param offset Offset to the desired pixel value.
14501        \note
14502        - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
14503          nearest pixel in the image instance, regarding the specified offset, i.e.
14504          - If \c offset<0, then \c img[0] is returned.
14505          - If \c offset>=img.size(), then \c img[img.size() - 1] is returned.
14506        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
14507          you are \e not sure about the validity of the specified pixel offset.
14508        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
14509      **/
14510     T& at(const int offset) {
14511       if (is_empty())
14512         throw CImgInstanceException(_cimg_instance
14513                                     "at(): Empty instance.",
14514                                     cimg_instance);
14515       return _at(offset);
14516     }
14517 
14518     T& _at(const int offset) {
14519       const unsigned int siz = (unsigned int)size();
14520       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
14521     }
14522 
14523     //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
14524     const T& at(const int offset) const {
14525       if (is_empty())
14526         throw CImgInstanceException(_cimg_instance
14527                                     "at(): Empty instance.",
14528                                     cimg_instance);
14529       return _at(offset);
14530     }
14531 
14532     const T& _at(const int offset) const {
14533       const unsigned int siz = (unsigned int)size();
14534       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
14535     }
14536 
14537     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
14538     /**
14539        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
14540        or to a specified default value in case of out-of-bounds access along the X-axis.
14541        \param x X-coordinate of the pixel value.
14542        \param y Y-coordinate of the pixel value.
14543        \param z Z-coordinate of the pixel value.
14544        \param c C-coordinate of the pixel value.
14545        \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
14546        \note
14547        - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value
14548          \c out_value.
14549        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
14550          you are \e not sure about the validity of the specified pixel coordinates.
14551        \warning
14552        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
14553     **/
14554     T& atX(const int x, const int y, const int z, const int c, const T& out_value) {
14555       return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
14556     }
14557 
14558     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
14559     T atX(const int x, const int y, const int z, const int c, const T& out_value) const {
14560       return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
14561     }
14562 
14563     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
14564     /**
14565        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
14566        or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
14567        \param x X-coordinate of the pixel value.
14568        \param y Y-coordinate of the pixel value.
14569        \param z Z-coordinate of the pixel value.
14570        \param c C-coordinate of the pixel value.
14571        \note
14572        - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
14573          nearest pixel in the image instance, regarding the specified X-coordinate.
14574        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
14575          you are \e not sure about the validity of the specified pixel coordinates.
14576        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14577          \c _at(int,int,int,int).
14578        \warning
14579        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
14580      **/
14581     T& atX(const int x, const int y=0, const int z=0, const int c=0) {
14582       if (is_empty())
14583         throw CImgInstanceException(_cimg_instance
14584                                     "atX(): Empty instance.",
14585                                     cimg_instance);
14586       return _atX(x,y,z,c);
14587     }
14588 
14589     T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
14590       return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
14591     }
14592 
14593     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
14594     const T& atX(const int x, const int y=0, const int z=0, const int c=0) const {
14595       if (is_empty())
14596         throw CImgInstanceException(_cimg_instance
14597                                     "atX(): Empty instance.",
14598                                     cimg_instance);
14599       return _atX(x,y,z,c);
14600     }
14601 
14602     const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const {
14603       return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
14604     }
14605 
14606     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
14607     /**
14608        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
14609     **/
14610     T& atXY(const int x, const int y, const int z, const int c, const T& out_value) {
14611       return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
14612     }
14613 
14614     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
14615     T atXY(const int x, const int y, const int z, const int c, const T& out_value) const {
14616       return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
14617     }
14618 
14619     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
14620     /**
14621        Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
14622        \note
14623        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14624          \c _atXY(int,int,int,int).
14625      **/
14626     T& atXY(const int x, const int y, const int z=0, const int c=0) {
14627       if (is_empty())
14628         throw CImgInstanceException(_cimg_instance
14629                                     "atXY(): Empty instance.",
14630                                     cimg_instance);
14631       return _atXY(x,y,z,c);
14632     }
14633 
14634     T& _atXY(const int x, const int y, const int z=0, const int c=0) {
14635       return (*this)(cimg::cut(x,0,width() - 1),
14636                      cimg::cut(y,0,height() - 1),z,c);
14637     }
14638 
14639     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
14640     const T& atXY(const int x, const int y, const int z=0, const int c=0) const {
14641       if (is_empty())
14642         throw CImgInstanceException(_cimg_instance
14643                                     "atXY(): Empty instance.",
14644                                     cimg_instance);
14645       return _atXY(x,y,z,c);
14646     }
14647 
14648     const T& _atXY(const int x, const int y, const int z=0, const int c=0) const {
14649       return (*this)(cimg::cut(x,0,width() - 1),
14650                      cimg::cut(y,0,height() - 1),z,c);
14651     }
14652 
14653     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
14654     /**
14655        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on
14656        X,Y and Z-coordinates.
14657     **/
14658     T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) {
14659       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
14660         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
14661     }
14662 
14663     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
14664     T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const {
14665       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
14666     }
14667 
14668     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
14669     /**
14670        Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
14671        \note
14672        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14673          \c _atXYZ(int,int,int,int).
14674     **/
14675     T& atXYZ(const int x, const int y, const int z, const int c=0) {
14676       if (is_empty())
14677         throw CImgInstanceException(_cimg_instance
14678                                     "atXYZ(): Empty instance.",
14679                                     cimg_instance);
14680       return _atXYZ(x,y,z,c);
14681     }
14682 
14683     T& _atXYZ(const int x, const int y, const int z, const int c=0) {
14684       return (*this)(cimg::cut(x,0,width() - 1),
14685                      cimg::cut(y,0,height() - 1),
14686                      cimg::cut(z,0,depth() - 1),c);
14687     }
14688 
14689     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
14690     const T& atXYZ(const int x, const int y, const int z, const int c=0) const {
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     const T& _atXYZ(const int x, const int y, const int z, const int c=0) const {
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 Dirichlet boundary conditions.
14705     /**
14706        Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all
14707        X,Y,Z and C-coordinates.
14708     **/
14709     T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) {
14710       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
14711         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
14712     }
14713 
14714     //! Access to a pixel value, using Dirichlet boundary conditions \const.
14715     T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const {
14716       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:
14717         (*this)(x,y,z,c);
14718     }
14719 
14720     //! Access to a pixel value, using Neumann boundary conditions.
14721     /**
14722        Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
14723        \note
14724        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14725          \c _atXYZC(int,int,int,int).
14726     **/
14727     T& atXYZC(const int x, const int y, const int z, const int c) {
14728       if (is_empty())
14729         throw CImgInstanceException(_cimg_instance
14730                                     "atXYZC(): Empty instance.",
14731                                     cimg_instance);
14732       return _atXYZC(x,y,z,c);
14733     }
14734 
14735     T& _atXYZC(const int x, const int y, const int z, const int c) {
14736       return (*this)(cimg::cut(x,0,width() - 1),
14737                      cimg::cut(y,0,height() - 1),
14738                      cimg::cut(z,0,depth() - 1),
14739                      cimg::cut(c,0,spectrum() - 1));
14740     }
14741 
14742     //! Access to a pixel value, using Neumann boundary conditions \const.
14743     const T& atXYZC(const int x, const int y, const int z, const int c) const {
14744       if (is_empty())
14745         throw CImgInstanceException(_cimg_instance
14746                                     "atXYZC(): Empty instance.",
14747                                     cimg_instance);
14748       return _atXYZC(x,y,z,c);
14749     }
14750 
14751     const T& _atXYZC(const int x, const int y, const int z, const int c) const {
14752       return (*this)(cimg::cut(x,0,width() - 1),
14753                      cimg::cut(y,0,height() - 1),
14754                      cimg::cut(z,0,depth() - 1),
14755                      cimg::cut(c,0,spectrum() - 1));
14756     }
14757 
14758     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
14759     /**
14760        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
14761        or a specified default value in case of out-of-bounds access along the X-axis.
14762        \param fx X-coordinate of the pixel value (float-valued).
14763        \param y Y-coordinate of the pixel value.
14764        \param z Z-coordinate of the pixel value.
14765        \param c C-coordinate of the pixel value.
14766        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
14767        \note
14768        - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by
14769          a linear interpolation along the X-axis, if corresponding coordinates are not integers.
14770        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
14771        \warning
14772        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
14773     **/
14774     Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
14775       const int
14776         x = (int)fx - (fx>=0?0:1), nx = x + 1;
14777       const float
14778         dx = fx - x;
14779       const Tfloat
14780         Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
14781       return Ic + dx*(In - Ic);
14782     }
14783 
14784     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
14785     /**
14786        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
14787        or the value of the nearest pixel location in the image instance in case of out-of-bounds access along
14788        the X-axis.
14789        \param fx X-coordinate of the pixel value (float-valued).
14790        \param y Y-coordinate of the pixel value.
14791        \param z Z-coordinate of the pixel value.
14792        \param c C-coordinate of the pixel value.
14793        \note
14794        - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns
14795          the value of the nearest pixel in the image instance, regarding the specified X-coordinate.
14796        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14797          \c _linear_atX(float,int,int,int).
14798        \warning
14799        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
14800     **/
14801     Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
14802       if (is_empty())
14803         throw CImgInstanceException(_cimg_instance
14804                                     "linear_atX(): Empty instance.",
14805                                     cimg_instance);
14806 
14807       return _linear_atX(fx,y,z,c);
14808     }
14809 
14810     Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
14811       const float
14812         nfx = cimg::cut(fx,0,width() - 1);
14813       const unsigned int
14814         x = (unsigned int)nfx;
14815       const float
14816         dx = nfx - x;
14817       const unsigned int
14818         nx = dx>0?x + 1:x;
14819       const Tfloat
14820         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
14821       return Ic + dx*(In - Ic);
14822     }
14823 
14824     //! Return pixel value, using linear interpolation and periodic boundary conditions for the X-coordinate.
14825     Tfloat linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
14826       if (is_empty())
14827         throw CImgInstanceException(_cimg_instance
14828                                     "linear_atX_p(): Empty instance.",
14829                                     cimg_instance);
14830 
14831       return _linear_atX_p(fx,y,z,c);
14832     }
14833 
14834     Tfloat _linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
14835       const float
14836         nfx = cimg::mod(fx,_width - 0.5f);
14837       const unsigned int
14838         x = (unsigned int)nfx;
14839       const float
14840         dx = nfx - x;
14841       const unsigned int
14842         nx = cimg::mod(x + 1,_width);
14843       const Tfloat
14844         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
14845       return Ic + dx*(In - Ic);
14846     }
14847 
14848     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
14849     /**
14850        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
14851        boundary checking are achieved both for X and Y-coordinates.
14852     **/
14853     Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
14854       const int
14855         x = (int)fx - (fx>=0?0:1), nx = x + 1,
14856         y = (int)fy - (fy>=0?0:1), ny = y + 1;
14857       const float
14858         dx = fx - x,
14859         dy = fy - y;
14860       const Tfloat
14861         Icc = (Tfloat)atXY(x,y,z,c,out_value),  Inc = (Tfloat)atXY(nx,y,z,c,out_value),
14862         Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
14863       return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
14864     }
14865 
14866     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
14867     /**
14868        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
14869        are achieved both for X and Y-coordinates.
14870        \note
14871        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14872          \c _linear_atXY(float,float,int,int).
14873     **/
14874     Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
14875       if (is_empty())
14876         throw CImgInstanceException(_cimg_instance
14877                                     "linear_atXY(): Empty instance.",
14878                                     cimg_instance);
14879 
14880       return _linear_atXY(fx,fy,z,c);
14881     }
14882 
14883     Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
14884       const float
14885         nfx = cimg::cut(fx,0,width() - 1),
14886         nfy = cimg::cut(fy,0,height() - 1);
14887       const unsigned int
14888         x = (unsigned int)nfx,
14889         y = (unsigned int)nfy;
14890       const float
14891         dx = nfx - x,
14892         dy = nfy - y;
14893       const unsigned int
14894         nx = dx>0?x + 1:x,
14895         ny = dy>0?y + 1:y;
14896       const Tfloat
14897         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
14898         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
14899       return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
14900     }
14901 
14902     //! Return pixel value, using linear interpolation and periodic boundary conditions for the X and Y-coordinates.
14903     Tfloat linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
14904       if (is_empty())
14905         throw CImgInstanceException(_cimg_instance
14906                                     "linear_atXY_p(): Empty instance.",
14907                                     cimg_instance);
14908 
14909       return _linear_atXY_p(fx,fy,z,c);
14910     }
14911 
14912     Tfloat _linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
14913       const float
14914         nfx = cimg::mod(fx,_width - 0.5f),
14915         nfy = cimg::mod(fy,_height - 0.5f);
14916       const unsigned int
14917         x = (unsigned int)nfx,
14918         y = (unsigned int)nfy;
14919       const float
14920         dx = nfx - x,
14921         dy = nfy - y;
14922       const unsigned int
14923         nx = cimg::mod(x + 1,_width),
14924         ny = cimg::mod(y + 1,_height);
14925       const Tfloat
14926         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
14927         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
14928       return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
14929     }
14930 
14931     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
14932     /**
14933        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
14934        boundary checking are achieved both for X,Y and Z-coordinates.
14935     **/
14936     Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
14937       const int
14938         x = (int)fx - (fx>=0?0:1), nx = x + 1,
14939         y = (int)fy - (fy>=0?0:1), ny = y + 1,
14940         z = (int)fz - (fz>=0?0:1), nz = z + 1;
14941       const float
14942         dx = fx - x,
14943         dy = fy - y,
14944         dz = fz - z;
14945       const Tfloat
14946         Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
14947         Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
14948         Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
14949         Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
14950       return Iccc +
14951         (Incc - Iccc +
14952          (Iccc + Innc - Icnc - Incc +
14953           (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
14954          (Iccc + Incn - Iccn - Incc)*dz)*dx +
14955         (Icnc - Iccc +
14956          (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
14957         (Iccn - Iccc)*dz;
14958     }
14959 
14960     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
14961     /**
14962        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
14963        are achieved both for X,Y and Z-coordinates.
14964        \note
14965        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14966          \c _linear_atXYZ(float,float,float,int).
14967     **/
14968     Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
14969       if (is_empty())
14970         throw CImgInstanceException(_cimg_instance
14971                                     "linear_atXYZ(): Empty instance.",
14972                                     cimg_instance);
14973 
14974       return _linear_atXYZ(fx,fy,fz,c);
14975     }
14976 
14977     Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
14978       const float
14979         nfx = cimg::cut(fx,0,width() - 1),
14980         nfy = cimg::cut(fy,0,height() - 1),
14981         nfz = cimg::cut(fz,0,depth() - 1);
14982       const unsigned int
14983         x = (unsigned int)nfx,
14984         y = (unsigned int)nfy,
14985         z = (unsigned int)nfz;
14986       const float
14987         dx = nfx - x,
14988         dy = nfy - y,
14989         dz = nfz - z;
14990       const unsigned int
14991         nx = dx>0?x + 1:x,
14992         ny = dy>0?y + 1:y,
14993         nz = dz>0?z + 1:z;
14994       const Tfloat
14995         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
14996         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
14997         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
14998         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
14999       return Iccc +
15000         (Incc - Iccc +
15001          (Iccc + Innc - Icnc - Incc +
15002           (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
15003          (Iccc + Incn - Iccn - Incc)*dz)*dx +
15004         (Icnc - Iccc +
15005          (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
15006         (Iccn - Iccc)*dz;
15007     }
15008 
15009     //! Return pixel value, using linear interpolation and periodic boundary conditions for the X,Y and Z-coordinates.
15010     Tfloat linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const {
15011       if (is_empty())
15012         throw CImgInstanceException(_cimg_instance
15013                                     "linear_atXYZ_p(): Empty instance.",
15014                                     cimg_instance);
15015 
15016       return _linear_atXYZ_p(fx,fy,fz,c);
15017     }
15018 
15019     Tfloat _linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const {
15020       const float
15021         nfx = cimg::mod(fx,_width - 0.5f),
15022         nfy = cimg::mod(fy,_height - 0.5f),
15023         nfz = cimg::mod(fz,_depth - 0.5f);
15024       const unsigned int
15025         x = (unsigned int)nfx,
15026         y = (unsigned int)nfy,
15027         z = (unsigned int)nfz;
15028       const float
15029         dx = nfx - x,
15030         dy = nfy - y,
15031         dz = nfz - z;
15032       const unsigned int
15033         nx = cimg::mod(x + 1,_width),
15034         ny = cimg::mod(y + 1,_height),
15035         nz = cimg::mod(z + 1,_depth);
15036       const Tfloat
15037         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
15038         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
15039         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
15040         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
15041       return Iccc +
15042         (Incc - Iccc +
15043          (Iccc + Innc - Icnc - Incc +
15044           (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
15045          (Iccc + Incn - Iccn - Incc)*dz)*dx +
15046         (Icnc - Iccc +
15047          (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
15048         (Iccn - Iccc)*dz;
15049     }
15050 
15051     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates.
15052     /**
15053        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
15054        boundary checking are achieved for all X,Y,Z and C-coordinates.
15055     **/
15056     Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const {
15057       const int
15058         x = (int)fx - (fx>=0?0:1), nx = x + 1,
15059         y = (int)fy - (fy>=0?0:1), ny = y + 1,
15060         z = (int)fz - (fz>=0?0:1), nz = z + 1,
15061         c = (int)fc - (fc>=0?0:1), nc = c + 1;
15062       const float
15063         dx = fx - x,
15064         dy = fy - y,
15065         dz = fz - z,
15066         dc = fc - c;
15067       const Tfloat
15068         Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
15069         Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
15070         Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
15071         Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
15072         Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
15073         Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
15074         Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
15075         Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
15076       return Icccc +
15077         dx*(Inccc - Icccc +
15078             dy*(Icccc + Inncc - Icncc - Inccc +
15079                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
15080                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
15081                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
15082                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
15083             dz*(Icccc + Incnc - Iccnc - Inccc +
15084                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
15085             dc*(Icccc + Inccn - Inccc - Icccn)) +
15086         dy*(Icncc - Icccc +
15087             dz*(Icccc + Icnnc - Iccnc - Icncc +
15088                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
15089             dc*(Icccc + Icncn - Icncc - Icccn)) +
15090         dz*(Iccnc - Icccc +
15091             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
15092         dc*(Icccn  -Icccc);
15093     }
15094 
15095     //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
15096     /**
15097        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
15098        are achieved for all X,Y,Z and C-coordinates.
15099        \note
15100        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15101          \c _linear_atXYZC(float,float,float,float).
15102     **/
15103     Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
15104       if (is_empty())
15105         throw CImgInstanceException(_cimg_instance
15106                                     "linear_atXYZC(): Empty instance.",
15107                                     cimg_instance);
15108 
15109       return _linear_atXYZC(fx,fy,fz,fc);
15110     }
15111 
15112     Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
15113       const float
15114         nfx = cimg::cut(fx,0,width() - 1),
15115         nfy = cimg::cut(fy,0,height() - 1),
15116         nfz = cimg::cut(fz,0,depth() - 1),
15117         nfc = cimg::cut(fc,0,spectrum() - 1);
15118       const unsigned int
15119         x = (unsigned int)nfx,
15120         y = (unsigned int)nfy,
15121         z = (unsigned int)nfz,
15122         c = (unsigned int)nfc;
15123       const float
15124         dx = nfx - x,
15125         dy = nfy - y,
15126         dz = nfz - z,
15127         dc = nfc - c;
15128       const unsigned int
15129         nx = dx>0?x + 1:x,
15130         ny = dy>0?y + 1:y,
15131         nz = dz>0?z + 1:z,
15132         nc = dc>0?c + 1:c;
15133       const Tfloat
15134         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
15135         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
15136         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
15137         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
15138         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
15139         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
15140         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
15141         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
15142       return Icccc +
15143         dx*(Inccc - Icccc +
15144             dy*(Icccc + Inncc - Icncc - Inccc +
15145                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
15146                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
15147                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
15148                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
15149             dz*(Icccc + Incnc - Iccnc - Inccc +
15150                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
15151             dc*(Icccc + Inccn - Inccc - Icccn)) +
15152         dy*(Icncc - Icccc +
15153             dz*(Icccc + Icnnc - Iccnc - Icncc +
15154                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
15155             dc*(Icccc + Icncn - Icncc - Icccn)) +
15156         dz*(Iccnc - Icccc +
15157             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
15158         dc*(Icccn - Icccc);
15159     }
15160 
15161     //! Return pixel value, using linear interpolation and periodic boundary conditions for all X,Y,Z and C-coordinates.
15162     Tfloat linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
15163       if (is_empty())
15164         throw CImgInstanceException(_cimg_instance
15165                                     "linear_atXYZC_p(): Empty instance.",
15166                                     cimg_instance);
15167 
15168       return _linear_atXYZC_p(fx,fy,fz,fc);
15169     }
15170 
15171     Tfloat _linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
15172       const float
15173         nfx = cimg::mod(fx,_width - 0.5f),
15174         nfy = cimg::mod(fy,_height - 0.5f),
15175         nfz = cimg::mod(fz,_depth - 0.5f),
15176         nfc = cimg::mod(fc,_spectrum - 0.5f);
15177       const unsigned int
15178         x = (unsigned int)nfx,
15179         y = (unsigned int)nfy,
15180         z = (unsigned int)nfz,
15181         c = (unsigned int)nfc;
15182       const float
15183         dx = nfx - x,
15184         dy = nfy - y,
15185         dz = nfz - z,
15186         dc = nfc - c;
15187       const unsigned int
15188         nx = cimg::mod(x + 1,_width),
15189         ny = cimg::mod(y + 1,_height),
15190         nz = cimg::mod(z + 1,_depth),
15191         nc = cimg::mod(c + 1,_spectrum);
15192       const Tfloat
15193         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
15194         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
15195         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
15196         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
15197         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
15198         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
15199         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
15200         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
15201       return Icccc +
15202         dx*(Inccc - Icccc +
15203             dy*(Icccc + Inncc - Icncc - Inccc +
15204                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
15205                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
15206                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
15207                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
15208             dz*(Icccc + Incnc - Iccnc - Inccc +
15209                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
15210             dc*(Icccc + Inccn - Inccc - Icccn)) +
15211         dy*(Icncc - Icccc +
15212             dz*(Icccc + Icnnc - Iccnc - Icncc +
15213                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
15214             dc*(Icccc + Icncn - Icncc - Icccn)) +
15215         dz*(Iccnc - Icccc +
15216             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
15217         dc*(Icccn - Icccc);
15218     }
15219 
15220     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
15221     /**
15222        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
15223        or a specified default value in case of out-of-bounds access along the X-axis.
15224        The cubic interpolation uses Hermite splines.
15225        \param fx d X-coordinate of the pixel value (float-valued).
15226        \param y Y-coordinate of the pixel value.
15227        \param z Z-coordinate of the pixel value.
15228        \param c C-coordinate of the pixel value.
15229        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
15230        \note
15231        - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is
15232          approximated by a \e cubic interpolation along the X-axis.
15233        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
15234        \warning
15235        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
15236     **/
15237     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
15238       const int
15239         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
15240       const float
15241         dx = fx - x;
15242       const Tfloat
15243         Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
15244         In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
15245       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));
15246     }
15247 
15248     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
15249     /**
15250        Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the
15251        min/max range of the datatype \c T.
15252     **/
15253     T cubic_atX_c(const float fx, const int y, const int z, const int c, const T& out_value) const {
15254       return cimg::type<T>::cut(cubic_atX(fx,y,z,c,out_value));
15255     }
15256 
15257     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
15258     /**
15259        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
15260        or the value of the nearest pixel location in the image instance in case of out-of-bounds access
15261        along the X-axis. The cubic interpolation uses Hermite splines.
15262        \param fx X-coordinate of the pixel value (float-valued).
15263        \param y Y-coordinate of the pixel value.
15264        \param z Z-coordinate of the pixel value.
15265        \param c C-coordinate of the pixel value.
15266        \note
15267        - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is
15268          approximated by a cubic interpolation along the X-axis.
15269        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15270          \c _cubic_atX(float,int,int,int).
15271        \warning
15272        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
15273     **/
15274     Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
15275       if (is_empty())
15276         throw CImgInstanceException(_cimg_instance
15277                                     "cubic_atX(): Empty instance.",
15278                                     cimg_instance);
15279       return _cubic_atX(fx,y,z,c);
15280     }
15281 
15282     Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
15283       const float
15284         nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1);
15285       const int
15286         x = (int)nfx;
15287       const float
15288         dx = nfx - x;
15289       const int
15290         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2;
15291       const Tfloat
15292         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
15293         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
15294       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));
15295     }
15296 
15297     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
15298     /**
15299        Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the
15300        min/max range of the datatype \c T.
15301     **/
15302     T cubic_atX_c(const float fx, const int y, const int z, const int c) const {
15303       return cimg::type<T>::cut(cubic_atX(fx,y,z,c));
15304     }
15305 
15306     T _cubic_atX_c(const float fx, const int y, const int z, const int c) const {
15307       return cimg::type<T>::cut(_cubic_atX(fx,y,z,c));
15308     }
15309 
15310     //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X-coordinate.
15311     Tfloat cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
15312       if (is_empty())
15313         throw CImgInstanceException(_cimg_instance
15314                                     "cubic_atX_p(): Empty instance.",
15315                                     cimg_instance);
15316       return _cubic_atX_p(fx,y,z,c);
15317     }
15318 
15319     Tfloat _cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
15320       const float
15321         nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f);
15322       const int
15323         x = (int)nfx;
15324       const float
15325         dx = nfx - x;
15326       const int
15327         px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width());
15328       const Tfloat
15329         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
15330         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
15331       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));
15332     }
15333 
15334     T cubic_atX_pc(const float fx, const int y, const int z, const int c) const {
15335       return cimg::type<T>::cut(cubic_atX_p(fx,y,z,c));
15336     }
15337 
15338     T _cubic_atX_pc(const float fx, const int y, const int z, const int c) const {
15339       return cimg::type<T>::cut(_cubic_atX_p(fx,y,z,c));
15340     }
15341 
15342     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
15343     /**
15344        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
15345        are achieved both for X and Y-coordinates.
15346     **/
15347     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
15348       const int
15349         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
15350         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
15351       const float dx = fx - x, dy = fy - y;
15352       const Tfloat
15353         Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value),
15354         Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
15355         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)),
15356         Ipc = (Tfloat)atXY(px,y,z,c,out_value),  Icc = (Tfloat)atXY(x, y,z,c,out_value),
15357         Inc = (Tfloat)atXY(nx,y,z,c,out_value),  Iac = (Tfloat)atXY(ax,y,z,c,out_value),
15358         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)),
15359         Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value),
15360         Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
15361         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)),
15362         Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value),
15363         Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
15364         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));
15365       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));
15366     }
15367 
15368     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates.
15369     /**
15370        Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the
15371        min/max range of the datatype \c T.
15372     **/
15373     T cubic_atXY_c(const float fx, const float fy, const int z, const int c, const T& out_value) const {
15374       return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c,out_value));
15375     }
15376 
15377     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
15378     /**
15379        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
15380        are achieved for both X and Y-coordinates.
15381        \note
15382        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15383        \c _cubic_atXY(float,float,int,int).
15384     **/
15385     Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
15386       if (is_empty())
15387         throw CImgInstanceException(_cimg_instance
15388                                     "cubic_atXY(): Empty instance.",
15389                                     cimg_instance);
15390       return _cubic_atXY(fx,fy,z,c);
15391     }
15392 
15393     Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
15394       const float
15395         nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1),
15396         nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1);
15397       const int x = (int)nfx, y = (int)nfy;
15398       const float dx = nfx - x, dy = nfy - y;
15399       const int
15400         px = x - 1<0?0:x - 1, nx = dx<=0?x:x + 1, ax = x + 2>=width()?width() - 1:x + 2,
15401         py = y - 1<0?0:y - 1, ny = dy<=0?y:y + 1, ay = y + 2>=height()?height() - 1:y + 2;
15402       const Tfloat
15403         Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
15404         Iap = (Tfloat)(*this)(ax,py,z,c),
15405         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)),
15406         Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
15407         Iac = (Tfloat)(*this)(ax,y,z,c),
15408         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)),
15409         Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
15410         Ian = (Tfloat)(*this)(ax,ny,z,c),
15411         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)),
15412         Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
15413         Iaa = (Tfloat)(*this)(ax,ay,z,c),
15414         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));
15415       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));
15416     }
15417 
15418     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates.
15419     /**
15420        Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the
15421        min/max range of the datatype \c T.
15422     **/
15423     T cubic_atXY_c(const float fx, const float fy, const int z, const int c) const {
15424       return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c));
15425     }
15426 
15427     T _cubic_atXY_c(const float fx, const float fy, const int z, const int c) const {
15428       return cimg::type<T>::cut(_cubic_atXY(fx,fy,z,c));
15429     }
15430 
15431     //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X and Y-coordinates.
15432     Tfloat cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
15433       if (is_empty())
15434         throw CImgInstanceException(_cimg_instance
15435                                     "cubic_atXY_p(): Empty instance.",
15436                                     cimg_instance);
15437       return _cubic_atXY_p(fx,fy,z,c);
15438     }
15439 
15440     Tfloat _cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
15441       const float
15442         nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
15443         nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f);
15444       const int x = (int)nfx, y = (int)nfy;
15445       const float dx = nfx - x, dy = nfy - y;
15446       const int
15447         px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
15448         py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height());
15449       const Tfloat
15450         Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
15451         Iap = (Tfloat)(*this)(ax,py,z,c),
15452         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)),
15453         Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
15454         Iac = (Tfloat)(*this)(ax,y,z,c),
15455         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)),
15456         Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
15457         Ian = (Tfloat)(*this)(ax,ny,z,c),
15458         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)),
15459         Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
15460         Iaa = (Tfloat)(*this)(ax,ay,z,c),
15461         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));
15462       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));
15463     }
15464 
15465     T cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const {
15466       return cimg::type<T>::cut(cubic_atXY_p(fx,fy,z,c));
15467     }
15468 
15469     T _cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const {
15470       return cimg::type<T>::cut(_cubic_atXY_p(fx,fy,z,c));
15471     }
15472 
15473     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
15474     /**
15475        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
15476        are achieved both for X,Y and Z-coordinates.
15477     **/
15478     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
15479       const int
15480         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
15481         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
15482         z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
15483       const float dx = fx - x, dy = fy - y, dz = fz - z;
15484       const Tfloat
15485         Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
15486         Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
15487         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
15488                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
15489         Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value),  Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
15490         Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value),  Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
15491         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
15492                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
15493         Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
15494         Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
15495         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
15496                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
15497         Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
15498         Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
15499         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
15500                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
15501         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
15502                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
15503         Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
15504         Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
15505         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
15506                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
15507         Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value),  Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
15508         Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),  Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
15509         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
15510                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
15511         Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
15512         Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
15513         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
15514                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
15515         Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
15516         Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
15517         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
15518                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
15519         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
15520                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
15521         Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
15522         Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
15523         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
15524                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
15525         Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value),  Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
15526         Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),  Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
15527         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
15528                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
15529         Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
15530         Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
15531         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
15532                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
15533         Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
15534         Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
15535         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
15536                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
15537         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
15538                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
15539         Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
15540         Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
15541         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
15542                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
15543         Ipca = (Tfloat)atXYZ(px,y,az,c,out_value),  Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
15544         Inca = (Tfloat)atXYZ(nx,y,az,c,out_value),  Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
15545         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
15546                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
15547         Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
15548         Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
15549         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
15550                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
15551         Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
15552         Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
15553         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
15554                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
15555         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
15556                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
15557       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));
15558     }
15559 
15560     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates.
15561     /**
15562        Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay
15563        in the min/max range of the datatype \c T.
15564     **/
15565     T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
15566       return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c,out_value));
15567     }
15568 
15569     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
15570     /**
15571        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
15572        are achieved both for X,Y and Z-coordinates.
15573        \note
15574        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15575          \c _cubic_atXYZ(float,float,float,int).
15576     **/
15577     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
15578       if (is_empty())
15579         throw CImgInstanceException(_cimg_instance
15580                                     "cubic_atXYZ(): Empty instance.",
15581                                     cimg_instance);
15582       return _cubic_atXYZ(fx,fy,fz,c);
15583     }
15584 
15585     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
15586       const float
15587         nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1),
15588         nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1),
15589         nfz = cimg::type<float>::is_nan(fz)?0:cimg::cut(fz,0,depth() - 1);
15590       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
15591       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
15592       const int
15593         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
15594         py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2,
15595         pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2;
15596       const Tfloat
15597         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
15598         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
15599         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
15600                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
15601         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
15602         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
15603         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
15604                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
15605         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
15606         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
15607         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
15608                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
15609         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
15610         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
15611         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
15612                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
15613         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
15614                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
15615         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
15616         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
15617         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
15618                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
15619         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
15620         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
15621         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
15622                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
15623         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
15624         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
15625         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
15626                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
15627         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
15628         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
15629         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
15630                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
15631         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
15632                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
15633         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
15634         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
15635         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
15636                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
15637         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
15638         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
15639         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
15640                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
15641         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
15642         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
15643         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
15644                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
15645         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
15646         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
15647         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
15648                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
15649         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
15650                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
15651         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
15652         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
15653         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
15654                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
15655         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
15656         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
15657         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
15658                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
15659         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
15660         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
15661         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
15662                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
15663         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
15664         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
15665         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
15666                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
15667         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
15668                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
15669       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));
15670     }
15671 
15672     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates.
15673     /**
15674        Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the
15675        min/max range of the datatype \c T.
15676     **/
15677     T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const {
15678       return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c));
15679     }
15680 
15681     T _cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const {
15682       return cimg::type<T>::cut(_cubic_atXYZ(fx,fy,fz,c));
15683     }
15684 
15685     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
15686     /**
15687        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
15688        are achieved both for X,Y and Z-coordinates.
15689        \note
15690        - If you know your image instance is \e not empty, you may rather use the slightly faster method
15691          \c _cubic_atXYZ(float,float,float,int).
15692     **/
15693     Tfloat cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
15694       if (is_empty())
15695         throw CImgInstanceException(_cimg_instance
15696                                     "cubic_atXYZ_p(): Empty instance.",
15697                                     cimg_instance);
15698       return _cubic_atXYZ_p(fx,fy,fz,c);
15699     }
15700 
15701     Tfloat _cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
15702       const float
15703         nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
15704         nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f),
15705         nfz = cimg::type<float>::is_nan(fz)?0:cimg::mod(fz,_depth - 0.5f);
15706       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
15707       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
15708       const int
15709         px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
15710         py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()),
15711         pz = cimg::mod(z - 1,depth()), nz = cimg::mod(z + 1,depth()), az = cimg::mod(z + 2,depth());
15712       const Tfloat
15713         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
15714         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
15715         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
15716                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
15717         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
15718         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
15719         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
15720                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
15721         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
15722         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
15723         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
15724                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
15725         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
15726         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
15727         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
15728                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
15729         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
15730                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
15731         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
15732         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
15733         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
15734                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
15735         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
15736         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
15737         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
15738                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
15739         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
15740         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
15741         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
15742                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
15743         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
15744         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
15745         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
15746                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
15747         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
15748                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
15749         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
15750         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
15751         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
15752                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
15753         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
15754         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
15755         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
15756                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
15757         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
15758         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
15759         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
15760                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
15761         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
15762         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
15763         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
15764                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
15765         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
15766                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
15767         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
15768         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
15769         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
15770                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
15771         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
15772         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
15773         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
15774                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
15775         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
15776         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
15777         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
15778                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
15779         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
15780         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
15781         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
15782                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
15783         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
15784                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
15785       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));
15786     }
15787 
15788     T cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const {
15789       return cimg::type<T>::cut(cubic_atXYZ_p(fx,fy,fz,c));
15790     }
15791 
15792     T _cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const {
15793       return cimg::type<T>::cut(_cubic_atXYZ_p(fx,fy,fz,c));
15794     }
15795 
15796     //! Set pixel value, using linear interpolation for the X-coordinates.
15797     /**
15798        Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that
15799        the value is spread amongst several neighbors if the pixel coordinates are float-valued.
15800        \param value Pixel value to set.
15801        \param fx X-coordinate of the pixel value (float-valued).
15802        \param y Y-coordinate of the pixel value.
15803        \param z Z-coordinate of the pixel value.
15804        \param c C-coordinate of the pixel value.
15805        \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image
15806          pixel(s).
15807        \return A reference to the current image instance.
15808        \note
15809        - Calling this method with out-of-bounds coordinates does nothing.
15810     **/
15811     CImg<T>& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0,
15812                             const bool is_added=false) {
15813       const int
15814         x = (int)fx - (fx>=0?0:1), nx = x + 1;
15815       const float
15816         dx = fx - x;
15817       if (y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum()) {
15818         if (x>=0 && x<width()) {
15819           const float w1 = 1 - dx, w2 = is_added?1:(1 - w1);
15820           (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
15821         }
15822         if (nx>=0 && nx<width()) {
15823           const float w1 = dx, w2 = is_added?1:(1 - w1);
15824           (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
15825         }
15826       }
15827       return *this;
15828     }
15829 
15830     //! Set pixel value, using linear interpolation for the X and Y-coordinates.
15831     /**
15832        Similar to set_linear_atX(const T&,float,int,int,int,bool), except that the linear interpolation
15833        is achieved both for X and Y-coordinates.
15834     **/
15835     CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
15836                              const bool is_added=false) {
15837       const int
15838         x = (int)fx - (fx>=0?0:1), nx = x + 1,
15839         y = (int)fy - (fy>=0?0:1), ny = y + 1;
15840       const float
15841         dx = fx - x,
15842         dy = fy - y;
15843       if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
15844         if (y>=0 && y<height()) {
15845           if (x>=0 && x<width()) {
15846             const float w1 = (1 - dx)*(1 - dy), w2 = is_added?1:(1 - w1);
15847             (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
15848           }
15849           if (nx>=0 && nx<width()) {
15850             const float w1 = dx*(1 - dy), w2 = is_added?1:(1 - w1);
15851             (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
15852           }
15853         }
15854         if (ny>=0 && ny<height()) {
15855           if (x>=0 && x<width()) {
15856             const float w1 = (1 - dx)*dy, w2 = is_added?1:(1 - w1);
15857             (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
15858           }
15859           if (nx>=0 && nx<width()) {
15860             const float w1 = dx*dy, w2 = is_added?1:(1 - w1);
15861             (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
15862           }
15863         }
15864       }
15865       return *this;
15866     }
15867 
15868     //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
15869     /**
15870        Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
15871        is achieved both for X,Y and Z-coordinates.
15872     **/
15873     CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
15874                               const bool is_added=false) {
15875       const int
15876         x = (int)fx - (fx>=0?0:1), nx = x + 1,
15877         y = (int)fy - (fy>=0?0:1), ny = y + 1,
15878         z = (int)fz - (fz>=0?0:1), nz = z + 1;
15879       const float
15880         dx = fx - x,
15881         dy = fy - y,
15882         dz = fz - z;
15883       if (c>=0 && c<spectrum()) {
15884         if (z>=0 && z<depth()) {
15885           if (y>=0 && y<height()) {
15886             if (x>=0 && x<width()) {
15887               const float w1 = (1 - dx)*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
15888               (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
15889             }
15890             if (nx>=0 && nx<width()) {
15891               const float w1 = dx*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
15892               (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
15893             }
15894           }
15895           if (ny>=0 && ny<height()) {
15896             if (x>=0 && x<width()) {
15897               const float w1 = (1 - dx)*dy*(1 - dz), w2 = is_added?1:(1 - w1);
15898               (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
15899             }
15900             if (nx>=0 && nx<width()) {
15901               const float w1 = dx*dy*(1 - dz), w2 = is_added?1:(1 - w1);
15902               (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
15903             }
15904           }
15905         }
15906         if (nz>=0 && nz<depth()) {
15907           if (y>=0 && y<height()) {
15908             if (x>=0 && x<width()) {
15909               const float w1 = (1 - dx)*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
15910               (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
15911             }
15912             if (nx>=0 && nx<width()) {
15913               const float w1 = dx*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
15914               (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
15915             }
15916           }
15917           if (ny>=0 && ny<height()) {
15918             if (x>=0 && x<width()) {
15919               const float w1 = (1 - dx)*dy*dz, w2 = is_added?1:(1 - w1);
15920               (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
15921             }
15922             if (nx>=0 && nx<width()) {
15923               const float w1 = dx*dy*dz, w2 = is_added?1:(1 - w1);
15924               (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
15925             }
15926           }
15927         }
15928       }
15929       return *this;
15930     }
15931 
15932     //! Return a C-string containing a list of all values of the image instance.
15933     /**
15934        Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
15935        of the image instance (written in base 10), separated by specified \c separator character.
15936        \param separator A \c char character which specifies the separator between values in the returned C-string.
15937        \param max_size Maximum size of the returned image (or \c 0 if no limits are set).
15938        \param format For float/double-values, tell the printf format used to generate the text representation
15939          of the numbers (or \c 0 for default representation).
15940        \note
15941        - The returned image is never empty.
15942        - For an empty image instance, the returned string is <tt>""</tt>.
15943        - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
15944        - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
15945          and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
15946     **/
15947     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0,
15948                              const char *const format=0) const {
15949       if (is_empty() || max_size==1) return CImg<charT>(1,1,1,1,0);
15950       CImgList<charT> items;
15951       CImg<charT> s_item(256); *s_item = 0;
15952       const T *ptrs = _data;
15953       unsigned int string_size = 0;
15954       const char *const _format = format?format:cimg::type<T>::format();
15955       for (ulongT off = 0, siz = size(); off<siz && (!max_size || string_size<max_size); ++off) {
15956         const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format,
15957                                                              cimg::type<T>::format(*(ptrs++)));
15958         CImg<charT> item(s_item._data,printed_size);
15959         item[printed_size - 1] = separator;
15960         item.move_to(items);
15961         if (max_size) string_size+=printed_size;
15962       }
15963       CImg<charT> res;
15964       (items>'x').move_to(res);
15965       if (max_size && res._width>=max_size) res.crop(0,max_size - 1);
15966       res.back() = 0;
15967       return res;
15968     }
15969 
15970     //@}
15971     //-------------------------------------
15972     //
15973     //! \name Instance Checking
15974     //@{
15975     //-------------------------------------
15976 
15977     //! Test shared state of the pixel buffer.
15978     /**
15979        Return \c true if image instance has a shared memory buffer, and \c false otherwise.
15980        \note
15981        - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
15982        - Most of the time, a \c CImg<T> image instance will \e not be shared.
15983        - A shared image can only be obtained by a limited set of constructors and methods (see list below).
15984     **/
15985     bool is_shared() const {
15986       return _is_shared;
15987     }
15988 
15989     //! Test if image instance is empty.
15990     /**
15991        Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions
15992        \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.
15993     **/
15994     bool is_empty() const {
15995       return !(_data && _width && _height && _depth && _spectrum);
15996     }
15997 
15998     //! Test if image instance contains a 'inf' value.
15999     /**
16000        Return \c true, if image instance contains a 'inf' value, and \c false otherwise.
16001     **/
16002     bool is_inf() const {
16003       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
16004       return false;
16005     }
16006 
16007     //! Test if image instance contains a NaN value.
16008     /**
16009        Return \c true, if image instance contains a NaN value, and \c false otherwise.
16010     **/
16011     bool is_nan() const {
16012       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
16013       return false;
16014     }
16015 
16016     //! Test if image width is equal to specified value.
16017     bool is_sameX(const unsigned int size_x) const {
16018       return _width==size_x;
16019     }
16020 
16021     //! Test if image width is equal to specified value.
16022     template<typename t>
16023     bool is_sameX(const CImg<t>& img) const {
16024       return is_sameX(img._width);
16025     }
16026 
16027     //! Test if image width is equal to specified value.
16028     bool is_sameX(const CImgDisplay& disp) const {
16029       return is_sameX(disp._width);
16030     }
16031 
16032     //! Test if image height is equal to specified value.
16033     bool is_sameY(const unsigned int size_y) const {
16034       return _height==size_y;
16035     }
16036 
16037     //! Test if image height is equal to specified value.
16038     template<typename t>
16039     bool is_sameY(const CImg<t>& img) const {
16040       return is_sameY(img._height);
16041     }
16042 
16043     //! Test if image height is equal to specified value.
16044     bool is_sameY(const CImgDisplay& disp) const {
16045       return is_sameY(disp._height);
16046     }
16047 
16048     //! Test if image depth is equal to specified value.
16049     bool is_sameZ(const unsigned int size_z) const {
16050       return _depth==size_z;
16051     }
16052 
16053     //! Test if image depth is equal to specified value.
16054     template<typename t>
16055     bool is_sameZ(const CImg<t>& img) const {
16056       return is_sameZ(img._depth);
16057     }
16058 
16059     //! Test if image spectrum is equal to specified value.
16060     bool is_sameC(const unsigned int size_c) const {
16061       return _spectrum==size_c;
16062     }
16063 
16064     //! Test if image spectrum is equal to specified value.
16065     template<typename t>
16066     bool is_sameC(const CImg<t>& img) const {
16067       return is_sameC(img._spectrum);
16068     }
16069 
16070     //! Test if image width and height are equal to specified values.
16071     /**
16072        Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
16073     **/
16074     bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
16075       return _width==size_x && _height==size_y;
16076     }
16077 
16078     //! Test if image width and height are the same as that of another image.
16079     /**
16080        Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
16081     **/
16082     template<typename t>
16083     bool is_sameXY(const CImg<t>& img) const {
16084       return is_sameXY(img._width,img._height);
16085     }
16086 
16087     //! Test if image width and height are the same as that of an existing display window.
16088     /**
16089        Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
16090     **/
16091     bool is_sameXY(const CImgDisplay& disp) const {
16092       return is_sameXY(disp._width,disp._height);
16093     }
16094 
16095     //! Test if image width and depth are equal to specified values.
16096     /**
16097        Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
16098     **/
16099     bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
16100       return _width==size_x && _depth==size_z;
16101     }
16102 
16103     //! Test if image width and depth are the same as that of another image.
16104     /**
16105        Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
16106     **/
16107     template<typename t>
16108     bool is_sameXZ(const CImg<t>& img) const {
16109       return is_sameXZ(img._width,img._depth);
16110     }
16111 
16112     //! Test if image width and spectrum are equal to specified values.
16113     /**
16114        Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
16115     **/
16116     bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
16117       return _width==size_x && _spectrum==size_c;
16118     }
16119 
16120     //! Test if image width and spectrum are the same as that of another image.
16121     /**
16122        Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16123     **/
16124     template<typename t>
16125     bool is_sameXC(const CImg<t>& img) const {
16126       return is_sameXC(img._width,img._spectrum);
16127     }
16128 
16129     //! Test if image height and depth are equal to specified values.
16130     /**
16131        Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
16132     **/
16133     bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
16134       return _height==size_y && _depth==size_z;
16135     }
16136 
16137     //! Test if image height and depth are the same as that of another image.
16138     /**
16139        Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
16140     **/
16141     template<typename t>
16142     bool is_sameYZ(const CImg<t>& img) const {
16143       return is_sameYZ(img._height,img._depth);
16144     }
16145 
16146     //! Test if image height and spectrum are equal to specified values.
16147     /**
16148        Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
16149     **/
16150     bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
16151       return _height==size_y && _spectrum==size_c;
16152     }
16153 
16154     //! Test if image height and spectrum are the same as that of another image.
16155     /**
16156        Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16157     **/
16158     template<typename t>
16159     bool is_sameYC(const CImg<t>& img) const {
16160       return is_sameYC(img._height,img._spectrum);
16161     }
16162 
16163     //! Test if image depth and spectrum are equal to specified values.
16164     /**
16165        Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
16166     **/
16167     bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
16168       return _depth==size_z && _spectrum==size_c;
16169     }
16170 
16171     //! Test if image depth and spectrum are the same as that of another image.
16172     /**
16173        Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16174     **/
16175     template<typename t>
16176     bool is_sameZC(const CImg<t>& img) const {
16177       return is_sameZC(img._depth,img._spectrum);
16178     }
16179 
16180     //! Test if image width, height and depth are equal to specified values.
16181     /**
16182        Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
16183     **/
16184     bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
16185       return is_sameXY(size_x,size_y) && _depth==size_z;
16186     }
16187 
16188     //! Test if image width, height and depth are the same as that of another image.
16189     /**
16190        Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
16191     **/
16192     template<typename t>
16193     bool is_sameXYZ(const CImg<t>& img) const {
16194       return is_sameXYZ(img._width,img._height,img._depth);
16195     }
16196 
16197     //! Test if image width, height and spectrum are equal to specified values.
16198     /**
16199        Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
16200     **/
16201     bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
16202       return is_sameXY(size_x,size_y) && _spectrum==size_c;
16203     }
16204 
16205     //! Test if image width, height and spectrum are the same as that of another image.
16206     /**
16207        Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16208     **/
16209     template<typename t>
16210     bool is_sameXYC(const CImg<t>& img) const {
16211       return is_sameXYC(img._width,img._height,img._spectrum);
16212     }
16213 
16214     //! Test if image width, depth and spectrum are equal to specified values.
16215     /**
16216        Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
16217     **/
16218     bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
16219       return is_sameXZ(size_x,size_z) && _spectrum==size_c;
16220     }
16221 
16222     //! Test if image width, depth and spectrum are the same as that of another image.
16223     /**
16224        Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16225     **/
16226     template<typename t>
16227     bool is_sameXZC(const CImg<t>& img) const {
16228       return is_sameXZC(img._width,img._depth,img._spectrum);
16229     }
16230 
16231     //! Test if image height, depth and spectrum are equal to specified values.
16232     /**
16233        Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
16234     **/
16235     bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
16236       return is_sameYZ(size_y,size_z) && _spectrum==size_c;
16237     }
16238 
16239     //! Test if image height, depth and spectrum are the same as that of another image.
16240     /**
16241        Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16242     **/
16243     template<typename t>
16244     bool is_sameYZC(const CImg<t>& img) const {
16245       return is_sameYZC(img._height,img._depth,img._spectrum);
16246     }
16247 
16248     //! Test if image width, height, depth and spectrum are equal to specified values.
16249     /**
16250        Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both
16251        verified.
16252     **/
16253     bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y,
16254                      const unsigned int size_z, const unsigned int size_c) const {
16255       return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
16256     }
16257 
16258     //! Test if image width, height, depth and spectrum are the same as that of another image.
16259     /**
16260        Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
16261     **/
16262     template<typename t>
16263     bool is_sameXYZC(const CImg<t>& img) const {
16264       return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
16265     }
16266 
16267     //! Test if specified coordinates are inside image bounds.
16268     /**
16269        Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance,
16270        and \c false otherwise.
16271        \param x X-coordinate of the pixel value.
16272        \param y Y-coordinate of the pixel value.
16273        \param z Z-coordinate of the pixel value.
16274        \param c C-coordinate of the pixel value.
16275        \note
16276        - Return \c true only if all these conditions are verified:
16277          - The image instance is \e not empty.
16278          - <tt>0<=x<=\ref width() - 1</tt>.
16279          - <tt>0<=y<=\ref height() - 1</tt>.
16280          - <tt>0<=z<=\ref depth() - 1</tt>.
16281          - <tt>0<=c<=\ref spectrum() - 1</tt>.
16282     **/
16283     bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
16284       return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
16285     }
16286 
16287     //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
16288     /**
16289        Return \c true, if specified reference refers to a pixel value inside bounds of the image instance,
16290        and \c false otherwise.
16291        \param pixel Reference to pixel value to test.
16292        \param[out] x X-coordinate of the pixel value, if test succeeds.
16293        \param[out] y Y-coordinate of the pixel value, if test succeeds.
16294        \param[out] z Z-coordinate of the pixel value, if test succeeds.
16295        \param[out] c C-coordinate of the pixel value, if test succeeds.
16296        \note
16297        - Useful to convert an offset to a buffer value into pixel value coordinates:
16298        \code
16299        const CImg<float> img(100,100,1,3);      // Construct a 100x100 RGB color image
16300        const unsigned long offset = 1249;       // Offset to the pixel (49,12,0,0)
16301        unsigned int x,y,z,c;
16302        if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates
16303          std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
16304                      offset,x,y,z,c);
16305        }
16306        \endcode
16307     **/
16308     template<typename t>
16309     bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
16310       const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
16311       const T *const ppixel = &pixel;
16312       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
16313       ulongT off = (ulongT)(ppixel - _data);
16314       const ulongT nc = off/whd;
16315       off%=whd;
16316       const ulongT nz = off/wh;
16317       off%=wh;
16318       const ulongT ny = off/_width, nx = off%_width;
16319       x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
16320       return true;
16321     }
16322 
16323     //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
16324     /**
16325        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
16326     **/
16327     template<typename t>
16328     bool contains(const T& pixel, t& x, t& y, t& z) const {
16329       const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
16330       const T *const ppixel = &pixel;
16331       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
16332       ulongT off = ((ulongT)(ppixel - _data))%whd;
16333       const ulongT nz = off/wh;
16334       off%=wh;
16335       const ulongT ny = off/_width, nx = off%_width;
16336       x = (t)nx; y = (t)ny; z = (t)nz;
16337       return true;
16338     }
16339 
16340     //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
16341     /**
16342        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
16343     **/
16344     template<typename t>
16345     bool contains(const T& pixel, t& x, t& y) const {
16346       const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum;
16347       const T *const ppixel = &pixel;
16348       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
16349       ulongT off = ((unsigned int)(ppixel - _data))%wh;
16350       const ulongT ny = off/_width, nx = off%_width;
16351       x = (t)nx; y = (t)ny;
16352       return true;
16353     }
16354 
16355     //! Test if pixel value is inside image bounds and get its X-coordinate.
16356     /**
16357        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
16358     **/
16359     template<typename t>
16360     bool contains(const T& pixel, t& x) const {
16361       const T *const ppixel = &pixel;
16362       if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false;
16363       x = (t)(((ulongT)(ppixel - _data))%_width);
16364       return true;
16365     }
16366 
16367     //! Test if pixel value is inside image bounds.
16368     /**
16369        Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
16370     **/
16371     bool contains(const T& pixel) const {
16372       const T *const ppixel = &pixel;
16373       return !is_empty() && ppixel>=_data && ppixel<_data + size();
16374     }
16375 
16376     //! Test if pixel buffers of instance and input images overlap.
16377     /**
16378        Return \c true, if pixel buffers attached to image instance and input image \c img overlap,
16379        and \c false otherwise.
16380        \param img Input image to compare with.
16381        \note
16382        - Buffer overlapping may happen when manipulating \e shared images.
16383        - If two image buffers overlap, operating on one of the image will probably modify the other one.
16384        - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
16385        \par Example
16386        \code
16387        const CImg<float>
16388          img1("reference.jpg"),             // Load RGB-color image
16389          img2 = img1.get_shared_channel(1); // Get shared version of the green channel
16390        if (img1.is_overlapped(img2)) {      // Test succeeds, 'img1' and 'img2' overlaps
16391          std::printf("Buffers overlap!\n");
16392        }
16393        \endcode
16394     **/
16395     template<typename t>
16396     bool is_overlapped(const CImg<t>& img) const {
16397       const ulongT csiz = size(), isiz = img.size();
16398       return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
16399     }
16400 
16401     //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3D object.
16402     /**
16403        Return \c true is the 3D object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
16404        valid 3D object, and \c false otherwise. The vertex coordinates are defined by the instance image.
16405        \param primitives List of primitives of the 3D object.
16406        \param colors List of colors of the 3D object.
16407        \param opacities List (or image) of opacities of the 3D object.
16408        \param full_check Tells if full checking of the 3D object must be performed.
16409        \param[out] error_message C-string to contain the error message, if the test does not succeed.
16410        \note
16411        - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of
16412          each 3D object component is checked.
16413        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
16414     **/
16415     template<typename tp, typename tc, typename to>
16416     bool is_object3d(const CImgList<tp>& primitives,
16417                      const CImgList<tc>& colors,
16418                      const to& opacities,
16419                      const bool full_check=true,
16420                      char *const error_message=0) const {
16421       if (error_message) *error_message = 0;
16422 
16423       // Check consistency for the particular case of an empty 3D object.
16424       if (is_empty()) {
16425         if (primitives || colors || opacities) {
16426           if (error_message) cimg_sprintf(error_message,
16427                                           "3D object (%u,%u) defines no vertices but %u primitives, "
16428                                           "%u colors and %lu opacities",
16429                                           _width,primitives._width,primitives._width,
16430                                           colors._width,(unsigned long)opacities.size());
16431           return false;
16432         }
16433         return true;
16434       }
16435 
16436       // Check consistency of vertices.
16437       if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions
16438         if (error_message) cimg_sprintf(error_message,
16439                                         "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)",
16440                                         _width,primitives._width,_width,_height,_depth,_spectrum);
16441         return false;
16442       }
16443       if (colors._width>primitives._width + 1) {
16444         if (error_message) cimg_sprintf(error_message,
16445                                         "3D object (%u,%u) defines %u colors",
16446                                         _width,primitives._width,colors._width);
16447         return false;
16448       }
16449       if (opacities.size()>primitives._width) {
16450         if (error_message) cimg_sprintf(error_message,
16451                                         "3D object (%u,%u) defines %lu opacities",
16452                                         _width,primitives._width,(unsigned long)opacities.size());
16453         return false;
16454       }
16455       if (!full_check) return true;
16456 
16457       // Check consistency of primitives.
16458       cimglist_for(primitives,l) {
16459         const CImg<tp>& primitive = primitives[l];
16460         const unsigned int psiz = (unsigned int)primitive.size();
16461         switch (psiz) {
16462         case 1 : { // Point
16463           const unsigned int i0 = (unsigned int)primitive(0);
16464           if (i0>=_width) {
16465             if (error_message) cimg_sprintf(error_message,
16466                                             "3D object (%u,%u) refers to invalid vertex index %u in "
16467                                             "point primitive [%u]",
16468                                             _width,primitives._width,i0,l);
16469             return false;
16470           }
16471         } break;
16472         case 5 : { // Sphere
16473           const unsigned int
16474             i0 = (unsigned int)primitive(0),
16475             i1 = (unsigned int)primitive(1);
16476           if (i0>=_width || i1>=_width) {
16477             if (error_message) cimg_sprintf(error_message,
16478                                             "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in "
16479                                             "sphere primitive [%u]",
16480                                             _width,primitives._width,i0,i1,l);
16481             return false;
16482           }
16483         } break;
16484         case 2 : case 6 : { // Segment
16485           const unsigned int
16486             i0 = (unsigned int)primitive(0),
16487             i1 = (unsigned int)primitive(1);
16488           if (i0>=_width || i1>=_width) {
16489             if (error_message) cimg_sprintf(error_message,
16490                                             "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in "
16491                                             "segment primitive [%u]",
16492                                             _width,primitives._width,i0,i1,l);
16493             return false;
16494           }
16495         } break;
16496         case 3 : case 9 : { // Triangle
16497           const unsigned int
16498             i0 = (unsigned int)primitive(0),
16499             i1 = (unsigned int)primitive(1),
16500             i2 = (unsigned int)primitive(2);
16501           if (i0>=_width || i1>=_width || i2>=_width) {
16502             if (error_message) cimg_sprintf(error_message,
16503                                             "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
16504                                             "triangle primitive [%u]",
16505                                             _width,primitives._width,i0,i1,i2,l);
16506             return false;
16507           }
16508         } break;
16509         case 4 : case 12 : { // Quadrangle
16510           const unsigned int
16511             i0 = (unsigned int)primitive(0),
16512             i1 = (unsigned int)primitive(1),
16513             i2 = (unsigned int)primitive(2),
16514             i3 = (unsigned int)primitive(3);
16515           if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
16516             if (error_message) cimg_sprintf(error_message,
16517                                             "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
16518                                             "quadrangle primitive [%u]",
16519                                             _width,primitives._width,i0,i1,i2,i3,l);
16520             return false;
16521           }
16522         } break;
16523         default :
16524           if (error_message) cimg_sprintf(error_message,
16525                                           "3D object (%u,%u) defines an invalid primitive [%u] of size %u",
16526                                           _width,primitives._width,l,(unsigned int)psiz);
16527           return false;
16528         }
16529       }
16530 
16531       // Check consistency of colors.
16532       cimglist_for(colors,c) {
16533         const CImg<tc>& color = colors[c];
16534         if (!color) {
16535           if (error_message) cimg_sprintf(error_message,
16536                                           "3D object (%u,%u) defines no color for primitive [%u]",
16537                                           _width,primitives._width,c);
16538           return false;
16539         }
16540       }
16541 
16542       // Check consistency of light texture.
16543       if (colors._width>primitives._width) {
16544         const CImg<tc> &light = colors.back();
16545         if (!light || light._depth>1) {
16546           if (error_message) cimg_sprintf(error_message,
16547                                           "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)",
16548                                           _width,primitives._width,light._width,
16549                                           light._height,light._depth,light._spectrum);
16550           return false;
16551         }
16552       }
16553 
16554       return true;
16555     }
16556 
16557     //! Test if image instance represents a valid serialization of a 3D object.
16558     /**
16559        Return \c true if the image instance represents a valid serialization of a 3D object, and \c false otherwise.
16560        \param full_check Tells if full checking of the instance must be performed.
16561        \param[out] error_message C-string to contain the error message, if the test does not succeed.
16562        \note
16563        - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of
16564          each 3D object component is checked.
16565        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
16566     **/
16567     bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
16568       if (error_message) *error_message = 0;
16569 
16570       // Check instance dimension and header.
16571       if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
16572         if (error_message) cimg_sprintf(error_message,
16573                                         "CImg3d has invalid dimensions (%u,%u,%u,%u)",
16574                                         _width,_height,_depth,_spectrum);
16575         return false;
16576       }
16577       const T *ptrs = _data, *const ptre = end();
16578       if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
16579           !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
16580         if (error_message) cimg_sprintf(error_message,
16581                                         "CImg3d header not found");
16582         return false;
16583       }
16584       const unsigned int
16585         nb_points = cimg::float2uint((float)*(ptrs++)),
16586         nb_primitives = cimg::float2uint((float)*(ptrs++));
16587 
16588       // Check consistency of number of vertices / primitives.
16589       if (!full_check) {
16590         const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
16591         if (_data + minimal_size>ptre) {
16592           if (error_message) cimg_sprintf(error_message,
16593                                           "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected",
16594                                           nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size);
16595           return false;
16596         }
16597       }
16598 
16599       // Check consistency of vertex data.
16600       if (!nb_points) {
16601         if (nb_primitives) {
16602           if (error_message) cimg_sprintf(error_message,
16603                                           "CImg3d (%u,%u) defines no vertices but %u primitives",
16604                                           nb_points,nb_primitives,nb_primitives);
16605           return false;
16606         }
16607         if (ptrs!=ptre) {
16608           if (error_message) cimg_sprintf(error_message,
16609                                           "CImg3d (%u,%u) is an empty object but contains %u value%s "
16610                                           "more than expected",
16611                                           nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
16612           return false;
16613         }
16614         return true;
16615       }
16616       if (ptrs + 3*nb_points>ptre) {
16617         if (error_message) cimg_sprintf(error_message,
16618                                         "CImg3d (%u,%u) defines only %u vertices data",
16619                                         nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3);
16620         return false;
16621       }
16622       ptrs+=3*nb_points;
16623 
16624       // Check consistency of primitive data.
16625       if (ptrs==ptre) {
16626         if (error_message) cimg_sprintf(error_message,
16627                                         "CImg3d (%u,%u) defines %u vertices but no primitive",
16628                                         nb_points,nb_primitives,nb_points);
16629         return false;
16630       }
16631 
16632       if (!full_check) return true;
16633 
16634       for (unsigned int p = 0; p<nb_primitives; ++p) {
16635         const unsigned int nb_inds = (unsigned int)*(ptrs++);
16636         switch (nb_inds) {
16637         case 1 : { // Point
16638           const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
16639           if (i0>=nb_points) {
16640             if (error_message) cimg_sprintf(error_message,
16641                                             "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]",
16642                                             nb_points,nb_primitives,i0,p);
16643             return false;
16644           }
16645         } break;
16646         case 5 : { // Sphere
16647           const unsigned int
16648             i0 = cimg::float2uint((float)*(ptrs++)),
16649             i1 = cimg::float2uint((float)*(ptrs++));
16650           ptrs+=3;
16651           if (i0>=nb_points || i1>=nb_points) {
16652             if (error_message) cimg_sprintf(error_message,
16653                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
16654                                             "sphere primitive [%u]",
16655                                             nb_points,nb_primitives,i0,i1,p);
16656             return false;
16657           }
16658         } break;
16659         case 2 : case 6 : { // Segment
16660           const unsigned int
16661             i0 = cimg::float2uint((float)*(ptrs++)),
16662             i1 = cimg::float2uint((float)*(ptrs++));
16663           if (nb_inds==6) ptrs+=4;
16664           if (i0>=nb_points || i1>=nb_points) {
16665             if (error_message) cimg_sprintf(error_message,
16666                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
16667                                             "segment primitive [%u]",
16668                                             nb_points,nb_primitives,i0,i1,p);
16669             return false;
16670           }
16671         } break;
16672         case 3 : case 9 : { // Triangle
16673           const unsigned int
16674             i0 = cimg::float2uint((float)*(ptrs++)),
16675             i1 = cimg::float2uint((float)*(ptrs++)),
16676             i2 = cimg::float2uint((float)*(ptrs++));
16677           if (nb_inds==9) ptrs+=6;
16678           if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
16679             if (error_message) cimg_sprintf(error_message,
16680                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
16681                                             "triangle primitive [%u]",
16682                                             nb_points,nb_primitives,i0,i1,i2,p);
16683             return false;
16684           }
16685         } break;
16686         case 4 : case 12 : { // Quadrangle
16687           const unsigned int
16688             i0 = cimg::float2uint((float)*(ptrs++)),
16689             i1 = cimg::float2uint((float)*(ptrs++)),
16690             i2 = cimg::float2uint((float)*(ptrs++)),
16691             i3 = cimg::float2uint((float)*(ptrs++));
16692           if (nb_inds==12) ptrs+=8;
16693           if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
16694             if (error_message) cimg_sprintf(error_message,
16695                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
16696                                             "quadrangle primitive [%u]",
16697                                             nb_points,nb_primitives,i0,i1,i2,i3,p);
16698             return false;
16699           }
16700         } break;
16701         default :
16702           if (error_message) cimg_sprintf(error_message,
16703                                           "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
16704                                           nb_points,nb_primitives,p,nb_inds);
16705           return false;
16706         }
16707         if (ptrs>ptre) {
16708           if (error_message) cimg_sprintf(error_message,
16709                                           "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
16710                                           "%u values missing",
16711                                           nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre));
16712           return false;
16713         }
16714       }
16715 
16716       // Check consistency of color data.
16717       if (ptrs==ptre) {
16718         if (error_message) cimg_sprintf(error_message,
16719                                         "CImg3d (%u,%u) defines no color/texture data",
16720                                         nb_points,nb_primitives);
16721         return false;
16722       }
16723       for (unsigned int c = 0; c<nb_primitives; ++c) {
16724         if (*(ptrs++)!=(T)-128) ptrs+=2;
16725         else if ((ptrs+=3)<ptre) {
16726           const unsigned int
16727             w = (unsigned int)*(ptrs - 3),
16728             h = (unsigned int)*(ptrs - 2),
16729             s = (unsigned int)*(ptrs - 1);
16730           if (!h && !s) {
16731             if (w>=c) {
16732               if (error_message) cimg_sprintf(error_message,
16733                                               "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u "
16734                                               "for primitive [%u]",
16735                                               nb_points,nb_primitives,w,c);
16736               return false;
16737             }
16738           } else ptrs+=w*h*s;
16739         }
16740         if (ptrs>ptre) {
16741           if (error_message) cimg_sprintf(error_message,
16742                                           "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
16743                                           "%u values missing",
16744                                           nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre));
16745           return false;
16746         }
16747       }
16748 
16749       // Check consistency of opacity data.
16750       if (ptrs==ptre) {
16751         if (error_message) cimg_sprintf(error_message,
16752                                         "CImg3d (%u,%u) defines no opacity data",
16753                                         nb_points,nb_primitives);
16754         return false;
16755       }
16756       for (unsigned int o = 0; o<nb_primitives; ++o) {
16757         if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
16758           const unsigned int
16759             w = (unsigned int)*(ptrs - 3),
16760             h = (unsigned int)*(ptrs - 2),
16761             s = (unsigned int)*(ptrs - 1);
16762           if (!h && !s) {
16763             if (w>=o) {
16764               if (error_message) cimg_sprintf(error_message,
16765                                               "CImg3d (%u,%u) refers to invalid shared opacity index %u "
16766                                               "for primitive [%u]",
16767                                               nb_points,nb_primitives,w,o);
16768               return false;
16769             }
16770           } else ptrs+=w*h*s;
16771         }
16772         if (ptrs>ptre) {
16773           if (error_message) cimg_sprintf(error_message,
16774                                           "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
16775                                           nb_points,nb_primitives,o);
16776           return false;
16777         }
16778       }
16779 
16780       // Check end of data.
16781       if (ptrs<ptre) {
16782         if (error_message) cimg_sprintf(error_message,
16783                                         "CImg3d (%u,%u) contains %u value%s more than expected",
16784                                         nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
16785         return false;
16786       }
16787       return true;
16788     }
16789 
16790     static bool _is_CImg3d(const T val, const char c) {
16791       return val>=(T)c && val<(T)(c + 1);
16792     }
16793 
16794     //@}
16795     //-------------------------------------
16796     //
16797     //! \name Mathematical Functions
16798     //@{
16799     //-------------------------------------
16800 
16801     // Define the math formula parser/compiler and expression evaluator.
16802     struct _cimg_math_parser {
16803       CImg<doubleT> mem;
16804       CImg<intT> memtype, memmerge;
16805       CImgList<ulongT> _code, &code, code_begin, code_end,
16806         _code_begin_t, &code_begin_t, _code_end_t, &code_end_t;
16807       CImg<ulongT> opcode;
16808       const CImg<ulongT> *p_code_end, *p_code;
16809       const CImg<ulongT> *const p_break;
16810 
16811       CImg<charT> expr, pexpr;
16812       const CImg<T>& imgin;
16813       const CImgList<T>& listin;
16814       CImg<T> &imgout;
16815       CImgList<T>& listout;
16816 
16817       CImg<doubleT> _img_stats, &img_stats, constcache_vals;
16818       CImgList<doubleT> _list_stats, &list_stats, _list_median, &list_median, _list_norm, &list_norm;
16819       CImg<uintT> mem_img_stats, constcache_inds;
16820 
16821       CImg<uintT> level, variable_pos, reserved_label;
16822       CImgList<charT> variable_def, macro_def, macro_body;
16823       CImgList<boolT> macro_body_is_string;
16824       char *user_macro;
16825 
16826       unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, break_type,
16827         constcache_size;
16828       bool is_parallelizable, is_end_code, is_fill, need_input_copy;
16829       double *result;
16830       cimg_uint64 rng;
16831       const char *const calling_function, *s_op, *ss_op;
16832       typedef double (*mp_func)(_cimg_math_parser&);
16833 
16834 #define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value?
16835 #define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value?
16836 #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value?
16837 #define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable?
16838 #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector?
16839 #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN)
16840 #define _cimg_mp_calling_function s_calling_function()._data
16841 #define _cimg_mp_op(s) s_op = s; ss_op = ss
16842 #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
16843 #define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char)
16844 #define _cimg_mp_check_constant_index(arg) check_constant_index(arg,ss,se,saved_char)
16845 #define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
16846 #define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char)
16847 #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp)
16848 #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; }
16849 #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan)
16850 #define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val)))
16851 #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op))
16852 #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1))
16853 #define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2))
16854 #define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3))
16855 #define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4))
16856 #define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5))
16857 #define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6))
16858 #define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7))
16859 #define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1))
16860 #define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2))
16861 #define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2))
16862 #define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2))
16863 #define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3))
16864 #define _cimg_mp_strerr \
16865   *se = saved_char; \
16866   for (s0 = ss; s0>expr._data && *s0!=';'; --s0) {} \
16867   if (*s0==';') ++s0; \
16868   while (cimg::is_blank(*s0)) ++s0; \
16869   cimg::strellipsize(s0,64)
16870 
16871       // Constructors / Destructors.
16872       ~_cimg_math_parser() {
16873         cimg::srand(rng);
16874       }
16875 
16876       _cimg_math_parser(const char *const expression, const char *const funcname=0,
16877                         const CImg<T>& img_input=CImg<T>::const_empty(), CImg<T> *const img_output=0,
16878                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0,
16879                         const bool _is_fill=false):
16880         code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t),
16881         p_break((CImg<ulongT>*)(cimg_ulong)-2),
16882         imgin(img_input),listin(list_inputs?*list_inputs:CImgList<T>::const_empty()),
16883         imgout(img_output?*img_output:CImg<T>::empty()),listout(list_outputs?*list_outputs:CImgList<T>::empty()),
16884         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0),
16885         mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),break_type(0),
16886         constcache_size(0),is_parallelizable(true),is_fill(_is_fill),need_input_copy(false),
16887         rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") {
16888 
16889 #if cimg_use_openmp!=0
16890         rng+=omp_get_thread_num();
16891 #endif
16892         if (!expression || !*expression)
16893           throw CImgArgumentException("[" cimg_appname "_math_parser] "
16894                                       "CImg<%s>::%s: Empty expression.",
16895                                       pixel_type(),_cimg_mp_calling_function);
16896         const char *_expression = expression;
16897         while (*_expression && (cimg::is_blank(*_expression) || *_expression==';')) ++_expression;
16898         CImg<charT>::string(_expression).move_to(expr);
16899         char *ps = &expr.back() - 1;
16900         while (ps>expr._data && (cimg::is_blank(*ps) || *ps==';')) --ps;
16901         *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1);
16902 
16903         // Ease the retrieval of previous non-space characters afterwards.
16904         pexpr.assign(expr._width);
16905         char c, *pe = pexpr._data;
16906         for (ps = expr._data, c = ' '; *ps; ++ps) {
16907           if (!cimg::is_blank(*ps)) c = *ps; else *ps = ' ';
16908           *(pe++) = c;
16909         }
16910         *pe = 0;
16911         level = get_level(expr);
16912 
16913         // Init constant values.
16914 #define _cimg_mp_interpolation (reserved_label[30]!=~0U?reserved_label[30]:0)
16915 #define _cimg_mp_boundary (reserved_label[31]!=~0U?reserved_label[31]:0)
16916 #define _cimg_mp_slot_t 17
16917 #define _cimg_mp_slot_nan 29
16918 #define _cimg_mp_slot_x 30
16919 #define _cimg_mp_slot_y 31
16920 #define _cimg_mp_slot_z 32
16921 #define _cimg_mp_slot_c 33
16922 
16923         mem.assign(96);
16924         for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10
16925         for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5
16926         mem[16] = 0.5;
16927         mem[_cimg_mp_slot_t] = 0; // thread_id
16928         mem[18] = (double)imgin._width; // w
16929         mem[19] = (double)imgin._height; // h
16930         mem[20] = (double)imgin._depth; // d
16931         mem[21] = (double)imgin._spectrum; // s
16932         mem[22] = (double)imgin._is_shared; // r
16933         mem[23] = (double)imgin._width*imgin._height; // wh
16934         mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd
16935         mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds
16936         mem[26] = (double)listin._width; // l
16937         mem[27] = std::exp(1.); // e
16938         mem[28] = cimg::PI; // pi
16939         mem[_cimg_mp_slot_nan] = cimg::type<double>::nan(); // nan
16940 
16941         // Set value property :
16942         // { -2 = other | -1 = variable | 0 = computation value |
16943         //    1 = compile-time constant | N>1 = constant ptr to vector[N-1] }.
16944         memtype.assign(mem._width,1,1,1,0);
16945         for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1;
16946         memtype[_cimg_mp_slot_t] = memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] =
16947           memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -2;
16948         mempos = _cimg_mp_slot_c + 1;
16949         variable_pos.assign(8);
16950 
16951         reserved_label.assign(128,1,1,1,~0U);
16952         // reserved_label[0-31] are used to store the memory index of these variables:
16953         // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv,
16954         // [8] = is, [9] = ip, [10] = ic, [11] = in, [12] = xm, [13] = ym, [14] = zm, [15] = cm, [16] = xM,
16955         // [17] = yM, [18] = zM, [19] = cM, [20] = i0...[29] = i9, [30] = interpolation, [31] = boundary
16956 
16957         // Compile expression into a sequence of opcodes.
16958         s_op = ""; ss_op = expr._data;
16959         const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false);
16960         if (!_cimg_mp_is_constant(ind_result)) {
16961           if (_cimg_mp_is_vector(ind_result))
16962             CImg<doubleT>(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true).
16963               fill(cimg::type<double>::nan());
16964           else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type<double>::nan();
16965         }
16966 
16967         // Free resources used for compiling expression and prepare evaluation.
16968         result_dim = _cimg_mp_size(ind_result);
16969         if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
16970         result = mem._data + ind_result;
16971         memtype.assign();
16972         constcache_vals.assign();
16973         constcache_inds.assign();
16974         level.assign();
16975         variable_pos.assign();
16976         reserved_label.assign();
16977         expr.assign();
16978         pexpr.assign();
16979         opcode.assign();
16980         opcode._is_shared = true;
16981 
16982         // Execute begin() bloc if any specified.
16983         if (code_begin) {
16984           mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
16985           p_code_end = code_begin.end();
16986           for (p_code = code_begin; p_code<p_code_end; ++p_code) {
16987             opcode._data = p_code->_data;
16988             const ulongT target = opcode[1];
16989             mem[target] = _cimg_mp_defunc(*this);
16990           }
16991         }
16992         p_code_end = code.end();
16993       }
16994 
16995       _cimg_math_parser():
16996         code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t),
16997         p_code_end(0),p_break((CImg<ulongT>*)(cimg_ulong)-2),
16998         imgin(CImg<T>::const_empty()),listin(CImgList<T>::const_empty()),
16999         imgout(CImg<T>::empty()),listout(CImgList<T>::empty()),
17000         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0),
17001         result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false),
17002         need_input_copy(false),rng(0),calling_function(0) {
17003         mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()()
17004         result = mem._data;
17005       }
17006 
17007       _cimg_math_parser(const _cimg_math_parser& mp):
17008         mem(mp.mem),code(mp.code),code_begin_t(mp.code_begin_t),code_end_t(mp.code_end_t),
17009         p_code_end(mp.p_code_end),p_break(mp.p_break),
17010         imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),
17011         img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm),
17012         debug_indent(0),result_dim(mp.result_dim),break_type(0),constcache_size(0),
17013         is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill),need_input_copy(mp.need_input_copy),
17014         result(mem._data + (mp.result - mp.mem._data)),rng((cimg::_rand(),cimg::rng())),calling_function(0) {
17015 
17016 #if cimg_use_openmp!=0
17017         mem[_cimg_mp_slot_t] = omp_get_thread_num();
17018         rng+=omp_get_thread_num();
17019 #endif
17020         opcode.assign();
17021         opcode._is_shared = true;
17022       }
17023 
17024       // Compilation procedure.
17025       unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref,
17026                            const bool is_critical) {
17027         if (depth>256) {
17028           cimg::strellipsize(expr,64);
17029           throw CImgArgumentException("[" cimg_appname "_math_parser] "
17030                                       "CImg<%s>::%s: Call stack overflow (infinite recursion?), "
17031                                       "in expression '%s%s%s'.",
17032                                       pixel_type(),_cimg_mp_calling_function,
17033                                       (ss - 4)>expr._data?"...":"",
17034                                       (ss - 4)>expr._data?ss - 4:expr._data,
17035                                       se<&expr.back()?"...":"");
17036         }
17037         char c1, c2;
17038 
17039         // Simplify expression when possible.
17040         do {
17041           c2 = 0;
17042           if (ss<se) {
17043             while (*ss && (cimg::is_blank(*ss) || *ss==';')) ++ss;
17044             while (se>ss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se;
17045           }
17046           while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) {
17047             ++ss; --se; c2 = 1;
17048           }
17049         } while (c2 && ss<se);
17050 
17051         if (se<=ss || !*ss) {
17052           cimg::strellipsize(expr,64);
17053           throw CImgArgumentException("[" cimg_appname "_math_parser] "
17054                                       "CImg<%s>::%s: %s%s Missing %s, in expression '%s%s%s'.",
17055                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
17056                                       *s_op=='F'?"argument":"item",
17057                                       (ss_op - 4)>expr._data?"...":"",
17058                                       (ss_op - 4)>expr._data?ss_op - 4:expr._data,
17059                                       ss_op + std::strlen(ss_op)<&expr.back()?"...":"");
17060         }
17061 
17062         static const size_t siz_ref = 7*sizeof(unsigned int);
17063         const char *const previous_s_op = s_op, *const previous_ss_op = ss_op;
17064         const unsigned int depth1 = depth + 1;
17065         unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
17066         char
17067           *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
17068           *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
17069           *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
17070           *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0;
17071         double val = 0, val1, val2;
17072         mp_func op;
17073 
17074         // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value
17075         // linked to the returned memory slot (reference that cannot be determined at compile time).
17076         // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) |
17077         //                   3 = image value (coordinates) | 4 = image value as a vector (offsets) |
17078         //                   5 = image value as a vector (coordinates) }.
17079         // Depending on p_ref[0], the remaining p_ref[k] have the following meaning:
17080         // When p_ref[0]==0, p_ref is actually unlinked.
17081         // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ].
17082         // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ].
17083         // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ].
17084         // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ].
17085         // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ].
17086         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; }
17087 
17088         const char saved_char = *se; *se = 0;
17089         const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
17090         bool is_sth, is_relative;
17091         CImg<uintT> ref;
17092         CImg<charT> variable_name;
17093         CImgList<ulongT> l_opcode;
17094 
17095         // Look for a single value or a pre-defined variable.
17096         int nb = 0;
17097         s = ss + (*ss=='+' || *ss=='-'?1:0);
17098         if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf
17099           is_sth = *ss=='-';
17100           if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
17101           else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
17102           if (nb==1 && is_sth) val = -val;
17103         } else if (*s=='0' && (s[1]=='x' || s[1]=='X')) { // Hexadecimal number
17104           is_sth = *ss=='-';
17105           if (cimg_sscanf(s + 2,"%x%c",&arg1,&sep)==1) {
17106             nb = 1;
17107             val = (double)arg1;
17108             if (is_sth) val = -val;
17109           }
17110         }
17111         if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0));
17112         if (nb==1) _cimg_mp_constant(val);
17113         if (nb==2 && sep=='%') _cimg_mp_constant(val/100);
17114 
17115         if (ss1==se) switch (*ss) { // One-char reserved variable
17116           case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c);
17117           case 'd' : _cimg_mp_return(reserved_label[(int)'d']!=~0U?reserved_label[(int)'d']:20);
17118           case 'e' : _cimg_mp_return(reserved_label[(int)'e']!=~0U?reserved_label[(int)'e']:27);
17119           case 'h' : _cimg_mp_return(reserved_label[(int)'h']!=~0U?reserved_label[(int)'h']:19);
17120           case 'k' :
17121             if (reserved_label[(int)'k']!=~0U) _cimg_mp_return(reserved_label[(int)'k']);
17122             pos = get_mem_img_index();
17123             if (pos!=~0U) _cimg_mp_return(pos);
17124             _cimg_mp_return_nan();
17125           case 'l' : _cimg_mp_return(reserved_label[(int)'l']!=~0U?reserved_label[(int)'l']:26);
17126           case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22);
17127           case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21);
17128           case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t);
17129           case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18);
17130           case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x);
17131           case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y);
17132           case 'z' : _cimg_mp_return(reserved_label[(int)'z']!=~0U?reserved_label[(int)'z']:_cimg_mp_slot_z);
17133           case 'u' :
17134             if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']);
17135             _cimg_mp_scalar2(mp_u,0,1);
17136           case 'g' :
17137             if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']);
17138             _cimg_mp_scalar0(mp_g);
17139           case 'i' :
17140             if (reserved_label[(int)'i']!=~0U) _cimg_mp_return(reserved_label[(int)'i']);
17141             _cimg_mp_scalar0(mp_i);
17142           case 'I' :
17143             _cimg_mp_op("Variable 'I'");
17144             if (reserved_label[(int)'I']!=~0U) _cimg_mp_return(reserved_label[(int)'I']);
17145             if (!imgin._spectrum) _cimg_mp_return(0);
17146             need_input_copy = true;
17147             pos = vector(imgin._spectrum);
17148             CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code);
17149             _cimg_mp_return(pos);
17150           case 'R' :
17151             if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']);
17152             need_input_copy = true;
17153             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0);
17154           case 'G' :
17155             if (reserved_label[(int)'G']!=~0U) _cimg_mp_return(reserved_label[(int)'G']);
17156             need_input_copy = true;
17157             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0);
17158           case 'B' :
17159             if (reserved_label[(int)'B']!=~0U) _cimg_mp_return(reserved_label[(int)'B']);
17160             need_input_copy = true;
17161             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0);
17162           case 'A' :
17163             if (reserved_label[(int)'A']!=~0U) _cimg_mp_return(reserved_label[(int)'A']);
17164             need_input_copy = true;
17165             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0);
17166           }
17167         else if (ss2==se) { // Two-chars reserved variable
17168           arg1 = arg2 = ~0U;
17169           if (*ss=='w' && *ss1=='h') // wh
17170             _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23);
17171           if (*ss=='p' && *ss1=='i') // pi
17172             _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28);
17173           if (*ss=='i') {
17174             if (*ss1>='0' && *ss1<='9') { // i0...i9
17175               pos = 20 + *ss1 - '0';
17176               if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
17177               need_input_copy = true;
17178               _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 20,0,0);
17179             }
17180             switch (*ss1) {
17181             case 'm' : arg1 = 4; arg2 = 0; break; // im
17182             case 'M' : arg1 = 5; arg2 = 1; break; // iM
17183             case 'a' : arg1 = 6; arg2 = 2; break; // ia
17184             case 'v' : arg1 = 7; arg2 = 3; break; // iv
17185             case 's' : arg1 = 8; arg2 = 12; break; // is
17186             case 'p' : arg1 = 9; arg2 = 13; break; // ip
17187             case 'c' : // ic
17188               if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]);
17189               if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0;
17190               _cimg_mp_return(mem_img_median);
17191               break;
17192             case 'n' : // in
17193               if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]);
17194               if (mem_img_norm==~0U) mem_img_norm = imgin?constant(imgin.magnitude()):0;
17195               _cimg_mp_return(mem_img_norm);
17196             }
17197           }
17198           else if (*ss1=='m') switch (*ss) {
17199             case 'x' : arg1 = 12; arg2 = 4; break; // xm
17200             case 'y' : arg1 = 13; arg2 = 5; break; // ym
17201             case 'z' : arg1 = 14; arg2 = 6; break; // zm
17202             case 'c' : arg1 = 15; arg2 = 7; break; // cm
17203             }
17204           else if (*ss1=='M') switch (*ss) {
17205             case 'x' : arg1 = 16; arg2 = 8; break; // xM
17206             case 'y' : arg1 = 17; arg2 = 9; break; // yM
17207             case 'z' : arg1 = 18; arg2 = 10; break; // zM
17208             case 'c' : arg1 = 19; arg2 = 11; break; // cM
17209             }
17210           if (arg1!=~0U) {
17211             if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
17212             if (!img_stats) {
17213               img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
17214               mem_img_stats.assign(1,14,1,1,~0U);
17215             }
17216             if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]);
17217             _cimg_mp_return(mem_img_stats[arg2]);
17218           }
17219         } else if (ss3==se) { // Three-chars reserved variable
17220           if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd
17221             _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24);
17222         } else if (ss4==se) { // Four-chars reserved variable
17223           if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds
17224             _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25);
17225         }
17226 
17227         pos = ~0U;
17228         is_sth = false;
17229         for (s0 = ss, s = ss1; s<se1; ++s)
17230           if (*s==';' && level[s - expr._data]==clevel) { // Separator ';'
17231             is_end_code = false;
17232             arg1 = compile(s0,s++,depth,0,is_critical);
17233             if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
17234             is_sth = true;
17235             while (*s && (cimg::is_blank(*s) || *s==';')) ++s;
17236             s0 = s;
17237           }
17238         if (is_sth) {
17239           is_end_code = false;
17240           arg1 = compile(s0,se,depth,p_ref,is_critical);
17241           if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
17242           _cimg_mp_return(pos);
17243         }
17244 
17245         // Declare / assign variable, vector value or image value.
17246         for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
17247           if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
17248               *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
17249               *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
17250               level[s - expr._data]==clevel) {
17251             variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0;
17252             cimg::strpare(variable_name,false,true);
17253             const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
17254             char *const ve1 = ss + l_variable_name - 1;
17255             _cimg_mp_op("Operator '='");
17256 
17257             // Assign image value (direct).
17258             if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') &&
17259                 (reserved_label[(int)*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[(int)*ss]))) {
17260               is_relative = *ss=='j' || *ss=='J';
17261 
17262               if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value
17263                 if (!is_critical) is_parallelizable = false;
17264                 if (*ss2=='#') { // Index specified
17265                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17266                   p1 = compile(ss3,s0++,depth1,0,is_critical);
17267                   _cimg_mp_check_list(true);
17268                 } else { p1 = ~0U; s0 = ss2; }
17269                 arg1 = compile(s0,ve1,depth1,0,is_critical); // Offset
17270                 _cimg_mp_check_type(arg1,0,1,0);
17271                 arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to assign
17272                 _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,0);
17273                 if (_cimg_mp_is_vector(arg2)) {
17274                   if (p1!=~0U) {
17275                     _cimg_mp_check_constant_index(p1);
17276                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17277                     p2 = listin[p3]._spectrum;
17278                   } else p2 = imgin._spectrum;
17279                   if (!p2) _cimg_mp_return(0);
17280                   _cimg_mp_check_type(arg2,2,2,p2);
17281                 } else p2 = 0;
17282 
17283                 if (p_ref) {
17284                   *p_ref = _cimg_mp_is_vector(arg2)?4:2;
17285                   p_ref[1] = p1;
17286                   p_ref[2] = (unsigned int)is_relative;
17287                   p_ref[3] = arg1;
17288                   if (_cimg_mp_is_vector(arg2))
17289                     set_variable_vector(arg2); // Prevent from being used in further optimization
17290                   else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
17291                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17292                 }
17293 
17294                 if (p1!=~0U) {
17295                   if (!listout) _cimg_mp_return(arg2);
17296                   if (*ss>='i')
17297                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17298                                         arg2,p1,arg1).move_to(code);
17299                   else if (_cimg_mp_is_scalar(arg2))
17300                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
17301                                         arg2,p1,arg1).move_to(code);
17302                   else
17303                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17304                                         arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code);
17305                 } else {
17306                   if (!imgout) _cimg_mp_return(arg2);
17307                   if (*ss>='i')
17308                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17309                                         arg2,arg1).move_to(code);
17310                   else if (_cimg_mp_is_scalar(arg2))
17311                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
17312                                         arg2,arg1).move_to(code);
17313                   else
17314                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17315                                         arg2,arg1,_cimg_mp_size(arg2)).move_to(code);
17316                 }
17317                 _cimg_mp_return(arg2);
17318               }
17319 
17320               if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value
17321                 if (!is_critical) is_parallelizable = false;
17322                 if (*ss2=='#') { // Index specified
17323                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17324                   p1 = compile(ss3,s0++,depth1,0,is_critical);
17325                   _cimg_mp_check_list(true);
17326                 } else { p1 = ~0U; s0 = ss2; }
17327                 arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17328                 arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17329                 arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17330                 arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
17331                 arg5 = compile(s + 1,se,depth1,0,is_critical); // Value to assign
17332                 _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,0);
17333                 if (s0<ve1) { // X or [ X,_Y,_Z,_C ]
17334                   s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17335                   arg1 = compile(s0,s1,depth1,0,is_critical);
17336                   if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17337                     p2 = _cimg_mp_size(arg1); // Vector size
17338                     ++arg1;
17339                     if (p2>1) {
17340                       arg2 = arg1 + 1;
17341                       if (p2>2) {
17342                         arg3 = arg2 + 1;
17343                         if (p2>3) arg4 = arg3 + 1;
17344                       }
17345                     }
17346                   } else if (s1<ve1) { // Y
17347                     s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17348                     arg2 = compile(s1,s2,depth1,0,is_critical);
17349                     if (s2<ve1) { // Z
17350                       s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17351                       arg3 = compile(s2,s3,depth1,0,is_critical);
17352                       if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,is_critical); // C
17353                     }
17354                   }
17355                 }
17356 
17357                 if (_cimg_mp_is_vector(arg5)) {
17358                   if (p1!=~0U) {
17359                     _cimg_mp_check_constant_index(p1);
17360                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17361                     p2 = listin[p3]._spectrum;
17362                   } else p2 = imgin._spectrum;
17363                   if (!p2) _cimg_mp_return(0);
17364                   _cimg_mp_check_type(arg5,2,2,p2);
17365                 } else p2 = 0;
17366 
17367 
17368                 if (p_ref) {
17369                   *p_ref = _cimg_mp_is_vector(arg5)?5:3;
17370                   p_ref[1] = p1;
17371                   p_ref[2] = (unsigned int)is_relative;
17372                   p_ref[3] = arg1;
17373                   p_ref[4] = arg2;
17374                   p_ref[5] = arg3;
17375                   p_ref[6] = arg4;
17376                   if (_cimg_mp_is_vector(arg5))
17377                     set_variable_vector(arg5); // Prevent from being used in further optimization
17378                   else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -2;
17379                   if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2;
17380                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17381                   if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
17382                   if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
17383                   if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2;
17384                 }
17385                 if (p1!=~0U) {
17386                   if (!listout) _cimg_mp_return(arg5);
17387                   if (*ss>='i')
17388                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17389                                         arg5,p1,arg1,arg2,arg3,arg4).move_to(code);
17390                   else if (_cimg_mp_is_scalar(arg5))
17391                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
17392                                         arg5,p1,arg1,arg2,arg3).move_to(code);
17393                   else
17394                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17395                                         arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
17396                 } else {
17397                   if (!imgout) _cimg_mp_return(arg5);
17398                   if (*ss>='i')
17399                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17400                                         arg5,arg1,arg2,arg3,arg4).move_to(code);
17401                   else if (_cimg_mp_is_scalar(arg5))
17402                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
17403                                         arg5,arg1,arg2,arg3).move_to(code);
17404                   else
17405                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17406                                         arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
17407                 }
17408                 _cimg_mp_return(arg5);
17409               }
17410             }
17411 
17412             // Assign vector value (direct).
17413             if (l_variable_name>3 && *ve1==']' && *ss!='[') {
17414               s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
17415               is_sth = true; // is_valid_variable_name?
17416               if (*ss>='0' && *ss<='9') is_sth = false;
17417               else for (ns = ss; ns<s0; ++ns)
17418                      if (!is_varchar(*ns)) { is_sth = false; break; }
17419               if (is_sth && s0>ss) {
17420                 variable_name[s0 - ss] = 0; // Remove brackets in variable name
17421                 get_variable_pos(variable_name,arg1,arg2);
17422                 arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot
17423                 if (arg1==~0U || _cimg_mp_is_scalar(arg1))
17424                   compile(ss,s0 - 1,depth1,0,is_critical); // Variable does not exist or is not a vector -> error
17425 
17426                 arg2 = compile(++s0,ve1,depth1,0,is_critical); // Index
17427                 arg3 = compile(s + 1,se,depth1,0,is_critical); // Value to assign
17428                 _cimg_mp_check_type(arg3,2,1,0);
17429 
17430                 if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly
17431                   nb = (int)mem[arg2];
17432                   if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) {
17433                     arg1+=nb + 1;
17434                     CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
17435                     _cimg_mp_return(arg1);
17436                   }
17437                   compile(ss,s,depth1,0,is_critical); // Out-of-bounds reference -> error
17438                 }
17439 
17440                 // Case of non-constant index -> return assigned value + linked reference
17441                 if (p_ref) {
17442                   *p_ref = 1;
17443                   p_ref[1] = arg1;
17444                   p_ref[2] = arg2;
17445                   if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; // Prevent from being used in further optimization
17446                   if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
17447                 }
17448                 CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2).
17449                   move_to(code);
17450                 _cimg_mp_return(arg3);
17451               }
17452             }
17453 
17454             // Assign user-defined macro.
17455             if (l_variable_name>2 && *ve1==')' && *ss!='(') {
17456               s0 = ve1; while (s0>ss && *s0!='(') --s0;
17457               is_sth = std::strncmp(variable_name,"debug(",6) &&
17458                 std::strncmp(variable_name,"print(",6); // is_valid_function_name?
17459               if (*ss>='0' && *ss<='9') is_sth = false;
17460               else for (ns = ss; ns<s0; ++ns)
17461                      if (!is_varchar(*ns)) { is_sth = false; break; }
17462 
17463               if (is_sth && s0>ss) { // Looks like a valid function declaration
17464                 s0 = variable_name._data + (s0 - ss);
17465                 *s0 = 0;
17466                 s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis
17467                 CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0);
17468                 ++s; while (*s && cimg::is_blank(*s)) ++s;
17469                 CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0);
17470 
17471                 p1 = 1; // Index of current parsed argument
17472                 for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments
17473                   if (p1>24) {
17474                     _cimg_mp_strerr;
17475                     cimg::strellipsize(variable_name,64);
17476                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
17477                                                 "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro "
17478                                                 "definition '%s()', in expression '%s%s%s'.",
17479                                                 pixel_type(),_cimg_mp_calling_function,s_op,
17480                                                 variable_name._data,
17481                                                 s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17482                   }
17483                   while (*s && cimg::is_blank(*s)) ++s;
17484                   if (*s==')' && p1==1) break; // Function has no arguments
17485 
17486                   s2 = s; // Start of the argument name
17487                   is_sth = true; // is_valid_argument_name?
17488                   if (*s>='0' && *s<='9') is_sth = false;
17489                   else for (ns = s; ns<s1 && *ns!=',' && !cimg::is_blank(*ns); ++ns)
17490                          if (!is_varchar(*ns)) { is_sth = false; break; }
17491                   s3 = ns; // End of the argument name
17492                   while (*ns && cimg::is_blank(*ns)) ++ns;
17493                   if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) {
17494                     _cimg_mp_strerr;
17495                     cimg::strellipsize(variable_name,64);
17496                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
17497                                                 "CImg<%s>::%s: %s: %s name specified for argument %u when defining "
17498                                                 "macro '%s()', in expression '%s%s%s'.",
17499                                                 pixel_type(),_cimg_mp_calling_function,s_op,
17500                                                 is_sth?"Empty":"Invalid",p1,
17501                                                 variable_name._data,
17502                                                 s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17503                   }
17504                   if (ns==s1 || *ns==',') { // New argument found
17505                     *s3 = 0;
17506                     p2 = (unsigned int)(s3 - s2); // Argument length
17507                     for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number
17508                       if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) ||
17509                             (ps + p2<macro_body[0].end() && is_varchar(*(ps + p2))))) {
17510                         if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign
17511                           *(ps - 1) = (char)p1;
17512                           if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs
17513                             std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1);
17514                             macro_body[0]._width-=p2 + 1;
17515                           } else { // Has pre number sign only
17516                             std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
17517                             macro_body[0]._width-=p2;
17518                           }
17519                         } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign
17520                           *(ps++) = (char)p1;
17521                           std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
17522                           macro_body[0]._width-=p2;
17523                         } else { // Not near a number sign
17524                           if (p2<3) {
17525                             ps-=(ulongT)macro_body[0]._data;
17526                             macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0);
17527                             ps+=(ulongT)macro_body[0]._data;
17528                           } else macro_body[0]._width-=p2 - 3;
17529                           std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3);
17530                           *(ps++) = '(';
17531                           *(ps++) = (char)p1;
17532                           *(ps++) = ')';
17533                         }
17534                       } else ++ps;
17535                     }
17536                   }
17537                 }
17538 
17539                 // Store number of arguments.
17540                 macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1);
17541 
17542                 // Detect parts of function body inside a string.
17543                 is_inside_string(macro_body[0]).move_to(macro_body_is_string,0);
17544                 _cimg_mp_return_nan();
17545               }
17546             }
17547 
17548             // Check if the variable name could be valid. If not, this is probably an lvalue assignment.
17549             is_sth = true; // is_valid_variable_name?
17550             const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6);
17551 
17552             s0 = variable_name._data;
17553             if (is_const) {
17554               s0+=6; while (cimg::is_blank(*s0)) ++s0;
17555               variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1);
17556             }
17557 
17558             if (*variable_name>='0' && *variable_name<='9') is_sth = false;
17559             else for (ns = variable_name._data; *ns; ++ns)
17560                    if (!is_varchar(*ns)) { is_sth = false; break; }
17561 
17562             // Assign variable (direct).
17563             if (is_sth) {
17564               get_variable_pos(variable_name,arg1,arg2);
17565               arg3 = compile(s + 1,se,depth1,0,is_critical);
17566               if (is_const) _cimg_mp_check_constant(arg3,2,0);
17567               arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
17568 
17569               if (arg1==~0U) { // Create new variable
17570                 if (_cimg_mp_is_vector(arg3)) { // Vector variable
17571                   arg1 = is_comp_vector(arg3)?arg3:vector_copy(arg3);
17572                   set_variable_vector(arg1); // Prevent from being used in further optimization
17573                 } else { // Scalar variable
17574                   if (is_const) arg1 = arg3;
17575                   else {
17576                     arg1 = _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3);
17577                     memtype[arg1] = -1;
17578                   }
17579                 }
17580                 if (arg2!=~0U) reserved_label[arg2] = arg1;
17581                 else {
17582                   if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
17583                   variable_pos[variable_def._width] = arg1;
17584                   variable_name.move_to(variable_def);
17585                 }
17586 
17587               } else { // Variable already exists -> assign a new value
17588                 if (is_const || _cimg_mp_is_constant(arg1)) {
17589                   _cimg_mp_strerr;
17590                   cimg::strellipsize(variable_name,64);
17591                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
17592                                               "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, "
17593                                               "in expression '%s%s%s'.",
17594                                               pixel_type(),_cimg_mp_calling_function,s_op,
17595                                               _cimg_mp_is_constant(arg1)?"already-defined ":"non-",
17596                                               variable_name._data,
17597                                               !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"",
17598                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17599                 }
17600                 _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1));
17601                 if (_cimg_mp_is_vector(arg1)) { // Vector
17602                   if (_cimg_mp_is_vector(arg3)) // From vector
17603                     CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg3,(ulongT)_cimg_mp_size(arg1)).
17604                       move_to(code);
17605                   else // From scalar
17606                     CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg3).
17607                       move_to(code);
17608                 } else // Scalar
17609                   CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
17610               }
17611               _cimg_mp_return(arg1);
17612             }
17613 
17614             // Assign lvalue (variable name was not valid for a direct assignment).
17615             arg1 = ~0U;
17616             is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
17617             if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment
17618 
17619             if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
17620               ref.assign(7);
17621               arg1 = compile(ss,s,depth1,ref,is_critical); // Lvalue slot
17622               arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to assign
17623 
17624               if (*ref==1) { // Vector value (scalar): V[k] = scalar
17625                 _cimg_mp_check_type(arg2,2,1,0);
17626                 arg3 = ref[1]; // Vector slot
17627                 arg4 = ref[2]; // Index
17628                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17629                 CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
17630                   move_to(code);
17631                 _cimg_mp_return(arg2);
17632               }
17633 
17634               if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar
17635                 if (!is_critical) is_parallelizable = false;
17636                 _cimg_mp_check_type(arg2,2,1,0);
17637                 p1 = ref[1]; // Index
17638                 is_relative = (bool)ref[2];
17639                 arg3 = ref[3]; // Offset
17640                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17641                 if (p1!=~0U) {
17642                   if (!listout) _cimg_mp_return(arg2);
17643                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17644                                       arg2,p1,arg3).move_to(code);
17645                 } else {
17646                   if (!imgout) _cimg_mp_return(arg2);
17647                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17648                                       arg2,arg3).move_to(code);
17649                 }
17650                 _cimg_mp_return(arg2);
17651               }
17652 
17653               if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar
17654                 if (!is_critical) is_parallelizable = false;
17655                 _cimg_mp_check_type(arg2,2,1,0);
17656                 p1 = ref[1]; // Index
17657                 is_relative = (bool)ref[2];
17658                 arg3 = ref[3]; // X
17659                 arg4 = ref[4]; // Y
17660                 arg5 = ref[5]; // Z
17661                 arg6 = ref[6]; // C
17662                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17663                 if (p1!=~0U) {
17664                   if (!listout) _cimg_mp_return(arg2);
17665                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17666                                       arg2,p1,arg3,arg4,arg5,arg6).move_to(code);
17667                 } else {
17668                   if (!imgout) _cimg_mp_return(arg2);
17669                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17670                                       arg2,arg3,arg4,arg5,arg6).move_to(code);
17671                 }
17672                 _cimg_mp_return(arg2);
17673               }
17674 
17675               if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value
17676                 if (!is_critical) is_parallelizable = false;
17677                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17678                 p1 = ref[1]; // Index
17679                 is_relative = (bool)ref[2];
17680                 arg3 = ref[3]; // Offset
17681                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17682                 if (p1!=~0U) {
17683                   if (!listout) _cimg_mp_return(arg2);
17684                   if (_cimg_mp_is_scalar(arg2))
17685                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
17686                                          arg2,p1,arg3).move_to(code);
17687                   else {
17688                     _cimg_mp_check_constant_index(p1);
17689                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17690                                          arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code);
17691                   }
17692 
17693                 } else {
17694                   if (!imgout) _cimg_mp_return(arg2);
17695                   if (_cimg_mp_is_scalar(arg2))
17696                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
17697                                         arg2,arg3).move_to(code);
17698                   else
17699                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17700                                         arg2,arg3,_cimg_mp_size(arg2)).move_to(code);
17701                 }
17702                 _cimg_mp_return(arg2);
17703               }
17704 
17705               if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value
17706                 if (!is_critical) is_parallelizable = false;
17707                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17708                 p1 = ref[1]; // Index
17709                 is_relative = (bool)ref[2];
17710                 arg3 = ref[3]; // X
17711                 arg4 = ref[4]; // Y
17712                 arg5 = ref[5]; // Z
17713                 if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17714                 if (p1!=~0U) {
17715                   if (!listout) _cimg_mp_return(arg2);
17716                   if (_cimg_mp_is_scalar(arg2))
17717                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
17718                                         arg2,p1,arg3,arg4,arg5).move_to(code);
17719                   else {
17720                     _cimg_mp_check_constant_index(p1);
17721                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17722                                          arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
17723                   }
17724 
17725                 } else {
17726                   if (!imgout) _cimg_mp_return(arg2);
17727                   if (_cimg_mp_is_scalar(arg2))
17728                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
17729                                         arg2,arg3,arg4,arg5).move_to(code);
17730                   else
17731                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17732                                         arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
17733                 }
17734                 _cimg_mp_return(arg2);
17735               }
17736 
17737               if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value
17738                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17739                 if (_cimg_mp_is_vector(arg2)) // From vector
17740                   CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
17741                     move_to(code);
17742                 else // From scalar
17743                   CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
17744                     move_to(code);
17745                 _cimg_mp_return(arg1);
17746               }
17747 
17748               if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s = scalar
17749                 _cimg_mp_check_type(arg2,2,1,0);
17750                 CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
17751                 _cimg_mp_return(arg1);
17752               }
17753             }
17754 
17755             // No assignment expressions match -> error
17756             _cimg_mp_strerr;
17757             cimg::strellipsize(variable_name,64);
17758             throw CImgArgumentException("[" cimg_appname "_math_parser] "
17759                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
17760                                         "in expression '%s%s%s'.",
17761                                         pixel_type(),_cimg_mp_calling_function,s_op,
17762                                         arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"",
17763                                         variable_name._data,
17764                                         s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17765           }
17766 
17767         // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++.
17768         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
17769           if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps &&
17770               level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=)
17771             _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='");
17772 
17773             ref.assign(7);
17774             arg1 = compile(ss,ns,depth1,ref,is_critical); // Vector slot
17775             arg2 = compile(s + 1,se,depth1,0,is_critical); // Right operand
17776             _cimg_mp_check_type(arg1,1,2,2);
17777             _cimg_mp_check_type(arg2,2,3,2);
17778             if (_cimg_mp_is_vector(arg2)) { // Complex **= complex
17779               if (*ps=='*')
17780                 CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code);
17781               else if (*ps=='/')
17782                 CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code);
17783               else
17784                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code);
17785             } else { // Complex **= scalar
17786               if (*ps=='*') {
17787                 if (arg2==1) _cimg_mp_return(arg1);
17788                 self_vector_s(arg1,mp_self_mul,arg2);
17789               } else if (*ps=='/') {
17790                 if (arg2==1) _cimg_mp_return(arg1);
17791                 self_vector_s(arg1,mp_self_div,arg2);
17792               } else {
17793                 if (arg2==1) _cimg_mp_return(arg1);
17794                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code);
17795               }
17796             }
17797 
17798             if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value
17799               if (!is_critical) is_parallelizable = false;
17800               p1 = ref[1]; // Index
17801               is_relative = (bool)ref[2];
17802               arg3 = ref[3]; // Offset
17803               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17804               if (p1!=~0U) {
17805                 if (!listout) _cimg_mp_return(arg1);
17806                 _cimg_mp_check_constant_index(p1);
17807                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17808                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
17809               } else {
17810                 if (!imgout) _cimg_mp_return(arg1);
17811                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17812                                      arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
17813               }
17814 
17815             } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value
17816               if (!is_critical) is_parallelizable = false;
17817               p1 = ref[1]; // Index
17818               is_relative = (bool)ref[2];
17819               arg3 = ref[3]; // X
17820               arg4 = ref[4]; // Y
17821               arg5 = ref[5]; // Z
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_constant_index(p1);
17826                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17827                                     arg1,p1,arg3,arg4,arg5,_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_Jxyz_v:mp_set_Ixyz_v),
17831                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17832               }
17833             }
17834 
17835             _cimg_mp_return(arg1);
17836           }
17837 
17838         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
17839           if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' ||
17840                           *ps=='&' || *ps=='^' || *ps=='|' ||
17841                           (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
17842               level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
17843             switch (*ps) {
17844             case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break;
17845             case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break;
17846             case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break;
17847             case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break;
17848             case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break;
17849             case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break;
17850             case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break;
17851             case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break;
17852             case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break;
17853             default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break;
17854             }
17855             s1 = *ps=='>' || *ps=='<'?ns:ps;
17856 
17857             ref.assign(7);
17858             arg1 = compile(ss,s1,depth1,ref,is_critical); // Variable slot
17859             arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to apply
17860 
17861             // Check for particular case to be simplified.
17862             if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1);
17863             if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1);
17864 
17865             // Apply operator on a copy to prevent modifying a constant or a variable.
17866             if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) {
17867               if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
17868               else arg1 = scalar1(mp_copy,arg1);
17869             }
17870 
17871             if (*ref==1) { // Vector value (scalar): V[k] += scalar
17872               _cimg_mp_check_type(arg2,2,1,0);
17873               arg3 = ref[1]; // Vector slot
17874               arg4 = ref[2]; // Index
17875               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17876               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17877               CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
17878                 move_to(code);
17879               _cimg_mp_return(arg1);
17880             }
17881 
17882             if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar
17883               if (!is_critical) is_parallelizable = false;
17884               _cimg_mp_check_type(arg2,2,1,0);
17885               p1 = ref[1]; // Index
17886               is_relative = (bool)ref[2];
17887               arg3 = ref[3]; // Offset
17888               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17889               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17890               if (p1!=~0U) {
17891                 if (!listout) _cimg_mp_return(arg1);
17892                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
17893                                     arg1,p1,arg3).move_to(code);
17894               } else {
17895                 if (!imgout) _cimg_mp_return(arg1);
17896                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
17897                                     arg1,arg3).move_to(code);
17898               }
17899               _cimg_mp_return(arg1);
17900             }
17901 
17902             if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar
17903               if (!is_critical) is_parallelizable = false;
17904               _cimg_mp_check_type(arg2,2,1,0);
17905               p1 = ref[1]; // Index
17906               is_relative = (bool)ref[2];
17907               arg3 = ref[3]; // X
17908               arg4 = ref[4]; // Y
17909               arg5 = ref[5]; // Z
17910               arg6 = ref[6]; // C
17911               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17912               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17913               if (p1!=~0U) {
17914                 if (!listout) _cimg_mp_return(arg1);
17915                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
17916                                     arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
17917               } else {
17918                 if (!imgout) _cimg_mp_return(arg1);
17919                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
17920                                     arg1,arg3,arg4,arg5,arg6).move_to(code);
17921               }
17922               _cimg_mp_return(arg1);
17923             }
17924 
17925             if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value
17926               if (!is_critical) is_parallelizable = false;
17927               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17928               p1 = ref[1]; // Index
17929               is_relative = (bool)ref[2];
17930               arg3 = ref[3]; // Offset
17931               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17932               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
17933               if (p1!=~0U) {
17934                 if (!listout) _cimg_mp_return(arg1);
17935                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
17936                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
17937               } else {
17938                 if (!imgout) _cimg_mp_return(arg1);
17939                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
17940                                     arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
17941               }
17942               _cimg_mp_return(arg1);
17943             }
17944 
17945             if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value
17946               if (!is_critical) is_parallelizable = false;
17947               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17948               p1 = ref[1]; // Index
17949               is_relative = (bool)ref[2];
17950               arg3 = ref[3]; // X
17951               arg4 = ref[4]; // Y
17952               arg5 = ref[5]; // Z
17953               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
17954               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
17955               if (p1!=~0U) {
17956                 if (!listout) _cimg_mp_return(arg1);
17957                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17958                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17959               } else {
17960                 if (!imgout) _cimg_mp_return(arg1);
17961                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17962                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17963               }
17964               _cimg_mp_return(arg1);
17965             }
17966 
17967             if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value
17968               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17969               if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector
17970               else self_vector_s(arg1,op,arg2); // Vector += scalar
17971               _cimg_mp_return(arg1);
17972             }
17973 
17974             if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s += scalar
17975               _cimg_mp_check_type(arg2,2,1,0);
17976               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
17977               _cimg_mp_return(arg1);
17978             }
17979 
17980             variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
17981             cimg::strpare(variable_name,false,true);
17982             _cimg_mp_strerr;
17983             cimg::strellipsize(variable_name,64);
17984             throw CImgArgumentException("[" cimg_appname "_math_parser] "
17985                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
17986                                         "in expression '%s%s%s'.",
17987                                         pixel_type(),_cimg_mp_calling_function,s_op,
17988                                         _cimg_mp_is_constant(arg1)?"const ":"",
17989                                         variable_name._data,
17990                                         s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
17991           }
17992 
17993         for (s = ss1; s<se1; ++s)
17994           if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'
17995             _cimg_mp_op("Operator '?:'");
17996             s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
17997             arg1 = compile(ss,s,depth1,0,is_critical);
17998             _cimg_mp_check_type(arg1,1,1,0);
17999             if (_cimg_mp_is_constant(arg1)) {
18000               if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,is_critical);
18001               else return *s1!=':'?0:compile(++s1,se,depth1,0,is_critical);
18002             }
18003             p2 = code._width;
18004             arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,is_critical);
18005             p3 = code._width;
18006             arg3 = *s1==':'?compile(++s1,se,depth1,0,is_critical):
18007               _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
18008             _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
18009             arg4 = _cimg_mp_size(arg2);
18010             if (arg4) pos = vector(arg4); else pos = scalar();
18011             CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
18012                                 p3 - p2,code._width - p3,arg4).move_to(code,p2);
18013             _cimg_mp_return(pos);
18014           }
18015 
18016         for (s = se3, ns = se2; s>ss; --s, --ns)
18017           if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||')
18018             _cimg_mp_op("Operator '||'");
18019             arg1 = compile(ss,s,depth1,0,is_critical);
18020             _cimg_mp_check_type(arg1,1,1,0);
18021             if (arg1>0 && arg1<=16) _cimg_mp_return(1);
18022             p2 = code._width;
18023             arg2 = compile(s + 2,se,depth1,0,is_critical);
18024             _cimg_mp_check_type(arg2,2,1,0);
18025             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18026               _cimg_mp_constant(mem[arg1] || mem[arg2]);
18027             if (!arg1) _cimg_mp_return(arg2);
18028             pos = scalar();
18029             CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2).
18030               move_to(code,p2);
18031             _cimg_mp_return(pos);
18032           }
18033 
18034         for (s = se3, ns = se2; s>ss; --s, --ns)
18035           if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&')
18036             _cimg_mp_op("Operator '&&'");
18037             arg1 = compile(ss,s,depth1,0,is_critical);
18038             _cimg_mp_check_type(arg1,1,1,0);
18039             if (!arg1) _cimg_mp_return(0);
18040             p2 = code._width;
18041             arg2 = compile(s + 2,se,depth1,0,is_critical);
18042             _cimg_mp_check_type(arg2,2,1,0);
18043             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18044               _cimg_mp_constant(mem[arg1] && mem[arg2]);
18045             if (arg1>0 && arg1<=16) _cimg_mp_return(arg2);
18046             pos = scalar();
18047             CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2).
18048               move_to(code,p2);
18049             _cimg_mp_return(pos);
18050           }
18051 
18052         for (s = se2; s>ss; --s)
18053           if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|')
18054             _cimg_mp_op("Operator '|'");
18055             arg1 = compile(ss,s,depth1,0,is_critical);
18056             arg2 = compile(s + 1,se,depth1,0,is_critical);
18057             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18058             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2);
18059             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18060               if (!arg2) _cimg_mp_return(arg1);
18061               _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2);
18062             }
18063             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18064               if (!arg1) _cimg_mp_return(arg2);
18065               _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2);
18066             }
18067             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18068               _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]);
18069             if (!arg2) _cimg_mp_return(arg1);
18070             if (!arg1) _cimg_mp_return(arg2);
18071             _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2);
18072           }
18073 
18074         for (s = se2; s>ss; --s)
18075           if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&')
18076             _cimg_mp_op("Operator '&'");
18077             arg1 = compile(ss,s,depth1,0,is_critical);
18078             arg2 = compile(s + 1,se,depth1,0,is_critical);
18079             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18080             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2);
18081             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2);
18082             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2);
18083             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18084               _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]);
18085             if (!arg1 || !arg2) _cimg_mp_return(0);
18086             _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2);
18087           }
18088 
18089         for (s = se3, ns = se2; s>ss; --s, --ns)
18090           if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=')
18091             _cimg_mp_op("Operator '!='");
18092             arg1 = compile(ss,s,depth1,0,is_critical);
18093             arg2 = compile(s + 2,se,depth1,0,is_critical);
18094             if (arg1==arg2) _cimg_mp_return(0);
18095             p1 = _cimg_mp_size(arg1);
18096             p2 = _cimg_mp_size(arg2);
18097             if (p1 || p2) {
18098               if (p1 && p2 && p1!=p2) _cimg_mp_return(1);
18099               pos = scalar();
18100               CImg<ulongT>::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
18101               _cimg_mp_return(pos);
18102             }
18103             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]);
18104             _cimg_mp_scalar2(mp_neq,arg1,arg2);
18105           }
18106 
18107         for (s = se3, ns = se2; s>ss; --s, --ns)
18108           if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==')
18109             _cimg_mp_op("Operator '=='");
18110             arg1 = compile(ss,s,depth1,0,is_critical);
18111             arg2 = compile(s + 2,se,depth1,0,is_critical);
18112             if (arg1==arg2) _cimg_mp_return(1);
18113             p1 = _cimg_mp_size(arg1);
18114             p2 = _cimg_mp_size(arg2);
18115             if (p1 || p2) {
18116               if (p1 && p2 && p1!=p2) _cimg_mp_return(0);
18117               pos = scalar();
18118               CImg<ulongT>::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
18119               _cimg_mp_return(pos);
18120             }
18121             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]);
18122             _cimg_mp_scalar2(mp_eq,arg1,arg2);
18123           }
18124 
18125         for (s = se3, ns = se2; s>ss; --s, --ns)
18126           if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=')
18127             _cimg_mp_op("Operator '<='");
18128             arg1 = compile(ss,s,depth1,0,is_critical);
18129             arg2 = compile(s + 2,se,depth1,0,is_critical);
18130             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18131             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2);
18132             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2);
18133             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2);
18134             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]);
18135             if (arg1==arg2) _cimg_mp_return(1);
18136             _cimg_mp_scalar2(mp_lte,arg1,arg2);
18137           }
18138 
18139         for (s = se3, ns = se2; s>ss; --s, --ns)
18140           if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=')
18141             _cimg_mp_op("Operator '>='");
18142             arg1 = compile(ss,s,depth1,0,is_critical);
18143             arg2 = compile(s + 2,se,depth1,0,is_critical);
18144             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18145             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2);
18146             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2);
18147             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2);
18148             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]);
18149             if (arg1==arg2) _cimg_mp_return(1);
18150             _cimg_mp_scalar2(mp_gte,arg1,arg2);
18151           }
18152 
18153         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
18154           if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<')
18155             _cimg_mp_op("Operator '<'");
18156             arg1 = compile(ss,s,depth1,0,is_critical);
18157             arg2 = compile(s + 1,se,depth1,0,is_critical);
18158             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18159             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2);
18160             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2);
18161             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2);
18162             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<mem[arg2]);
18163             if (arg1==arg2) _cimg_mp_return(0);
18164             _cimg_mp_scalar2(mp_lt,arg1,arg2);
18165           }
18166 
18167         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
18168           if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>')
18169             _cimg_mp_op("Operator '>'");
18170             arg1 = compile(ss,s,depth1,0,is_critical);
18171             arg2 = compile(s + 1,se,depth1,0,is_critical);
18172             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18173             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2);
18174             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2);
18175             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2);
18176             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]);
18177             if (arg1==arg2) _cimg_mp_return(0);
18178             _cimg_mp_scalar2(mp_gt,arg1,arg2);
18179           }
18180 
18181         for (s = se3, ns = se2; s>ss; --s, --ns)
18182           if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<')
18183             _cimg_mp_op("Operator '<<'");
18184             arg1 = compile(ss,s,depth1,0,is_critical);
18185             arg2 = compile(s + 2,se,depth1,0,is_critical);
18186             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18187             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
18188               _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2);
18189             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18190               if (!arg2) _cimg_mp_return(arg1);
18191               _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2);
18192             }
18193             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
18194               _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2);
18195             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18196               _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]);
18197             if (!arg1) _cimg_mp_return(0);
18198             if (!arg2) _cimg_mp_return(arg1);
18199             _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2);
18200           }
18201 
18202         for (s = se3, ns = se2; s>ss; --s, --ns)
18203           if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>')
18204             _cimg_mp_op("Operator '>>'");
18205             arg1 = compile(ss,s,depth1,0,is_critical);
18206             arg2 = compile(s + 2,se,depth1,0,is_critical);
18207             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18208             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
18209               _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2);
18210             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18211               if (!arg2) _cimg_mp_return(arg1);
18212               _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2);
18213             }
18214             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
18215               _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2);
18216             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18217               _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]);
18218             if (!arg1) _cimg_mp_return(0);
18219             if (!arg2) _cimg_mp_return(arg1);
18220             _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2);
18221           }
18222 
18223         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
18224           if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
18225               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
18226               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
18227                                                                                      *(ps - 1)<='9')))) &&
18228               level[s - expr._data]==clevel) { // Addition ('+')
18229             _cimg_mp_op("Operator '+'");
18230             arg1 = compile(ss,s,depth1,0,is_critical);
18231             arg2 = compile(s + 1,se,depth1,0,is_critical);
18232             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18233             if (!arg2) _cimg_mp_return(arg1);
18234             if (!arg1) _cimg_mp_return(arg2);
18235             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2);
18236             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2);
18237             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2);
18238             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]);
18239             if (code) { // Try to spot linear case 'a*b + c'
18240               CImg<ulongT> &pop = code.back();
18241               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
18242                 arg3 = (unsigned int)pop[1];
18243                 arg4 = (unsigned int)pop[2];
18244                 arg5 = (unsigned int)pop[3];
18245                 code.remove();
18246                 CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
18247                 _cimg_mp_return(arg3);
18248               }
18249             }
18250             if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1);
18251             if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2);
18252             _cimg_mp_scalar2(mp_add,arg1,arg2);
18253           }
18254 
18255         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
18256           if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
18257               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
18258               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
18259                                                                                      *(ps - 1)<='9')))) &&
18260               level[s - expr._data]==clevel) { // Subtraction ('-')
18261             _cimg_mp_op("Operator '-'");
18262             arg1 = compile(ss,s,depth1,0,is_critical);
18263             arg2 = compile(s + 1,se,depth1,0,is_critical);
18264             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18265             if (!arg2) _cimg_mp_return(arg1);
18266             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2);
18267             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2);
18268             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18269               if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2);
18270               _cimg_mp_vector2_sv(mp_sub,arg1,arg2);
18271             }
18272             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]);
18273             if (!arg1) _cimg_mp_scalar1(mp_minus,arg2);
18274             if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b'
18275               CImg<ulongT> &pop = code.back();
18276               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
18277                 arg3 = (unsigned int)pop[1];
18278                 arg4 = (unsigned int)pop[2];
18279                 arg5 = (unsigned int)pop[3];
18280                 code.remove();
18281                 CImg<ulongT>::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right),
18282                                      arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code);
18283                 _cimg_mp_return(arg3);
18284               }
18285             }
18286             if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1);
18287             _cimg_mp_scalar2(mp_sub,arg1,arg2);
18288           }
18289 
18290         for (s = se3, ns = se2; s>ss; --s, --ns)
18291           if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**')
18292             _cimg_mp_op("Operator '**'");
18293             arg1 = compile(ss,s,depth1,0,is_critical);
18294             arg2 = compile(s + 2,se,depth1,0,is_critical);
18295             _cimg_mp_check_type(arg1,1,3,2);
18296             _cimg_mp_check_type(arg2,2,3,2);
18297             if (arg2==1) _cimg_mp_return(arg1);
18298             if (arg1==1) _cimg_mp_return(arg2);
18299             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
18300               pos = vector(2);
18301               CImg<ulongT>::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code);
18302               _cimg_mp_return(pos);
18303             }
18304             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
18305             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
18306             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
18307             if (!arg1 || !arg2) _cimg_mp_return(0);
18308             _cimg_mp_scalar2(mp_mul,arg1,arg2);
18309           }
18310 
18311         for (s = se3, ns = se2; s>ss; --s, --ns)
18312           if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//')
18313             _cimg_mp_op("Operator '//'");
18314             arg1 = compile(ss,s,depth1,0,is_critical);
18315             arg2 = compile(s + 2,se,depth1,0,is_critical);
18316             _cimg_mp_check_type(arg1,1,3,2);
18317             _cimg_mp_check_type(arg2,2,3,2);
18318             if (arg2==1) _cimg_mp_return(arg1);
18319             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
18320               pos = vector(2);
18321               CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code);
18322               _cimg_mp_return(pos);
18323             }
18324             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18325             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18326               pos = vector(2);
18327               CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code);
18328               _cimg_mp_return(pos);
18329             }
18330             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
18331             if (!arg1) _cimg_mp_return(0);
18332             _cimg_mp_scalar2(mp_div,arg1,arg2);
18333           }
18334 
18335         for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*')
18336             _cimg_mp_op("Operator '*'");
18337             arg1 = compile(ss,s,depth1,0,is_critical);
18338             arg2 = compile(s + 1,se,depth1,0,is_critical);
18339             p2 = _cimg_mp_size(arg2);
18340             if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication
18341               pos = vector(p2);
18342               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code);
18343               _cimg_mp_return(pos);
18344             }
18345             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18346             if (arg2==1) _cimg_mp_return(arg1);
18347             if (arg1==1) _cimg_mp_return(arg2);
18348             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2);
18349             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
18350             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
18351             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
18352 
18353             if (code) { // Try to spot double multiplication 'a*b*c'
18354               CImg<ulongT> &pop = code.back();
18355               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
18356                 arg3 = (unsigned int)pop[1];
18357                 arg4 = (unsigned int)pop[2];
18358                 arg5 = (unsigned int)pop[3];
18359                 code.remove();
18360                 CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
18361                 _cimg_mp_return(arg3);
18362               }
18363             }
18364             if (!arg1 || !arg2) _cimg_mp_return(0);
18365             _cimg_mp_scalar2(mp_mul,arg1,arg2);
18366           }
18367 
18368         for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/')
18369             _cimg_mp_op("Operator '/'");
18370             arg1 = compile(ss,s,depth1,0,is_critical);
18371             arg2 = compile(s + 1,se,depth1,0,is_critical);
18372             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18373             if (arg2==1) _cimg_mp_return(arg1);
18374             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2);
18375             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18376             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2);
18377             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
18378             if (!arg1) _cimg_mp_return(0);
18379             _cimg_mp_scalar2(mp_div,arg1,arg2);
18380           }
18381 
18382         for (s = se2, ns = se1; s>ss; --s, --ns)
18383           if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%')
18384             _cimg_mp_op("Operator '%'");
18385             arg1 = compile(ss,s,depth1,0,is_critical);
18386             arg2 = compile(s + 1,se,depth1,0,is_critical);
18387             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18388             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2);
18389             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2);
18390             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2);
18391             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18392               _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2]));
18393             _cimg_mp_scalar2(mp_modulo,arg1,arg2);
18394           }
18395 
18396         if (se1>ss) {
18397           if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+')
18398             _cimg_mp_op("Operator '+'");
18399             _cimg_mp_return(compile(ss1,se,depth1,0,is_critical));
18400           }
18401 
18402           if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-')
18403             _cimg_mp_op("Operator '-'");
18404             arg1 = compile(ss1,se,depth1,0,is_critical);
18405             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1);
18406             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]);
18407             _cimg_mp_scalar1(mp_minus,arg1);
18408           }
18409 
18410           if (*ss=='!') { // Logical not ('!')
18411             _cimg_mp_op("Operator '!'");
18412             if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)'
18413               arg1 = compile(ss2,se,depth1,0,is_critical);
18414               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
18415               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
18416               _cimg_mp_scalar1(mp_bool,arg1);
18417             }
18418             arg1 = compile(ss1,se,depth1,0,is_critical);
18419             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1);
18420             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]);
18421             _cimg_mp_scalar1(mp_logical_not,arg1);
18422           }
18423 
18424           if (*ss=='~') { // Bitwise not ('~')
18425             _cimg_mp_op("Operator '~'");
18426             arg1 = compile(ss1,se,depth1,0,is_critical);
18427             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1);
18428             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]);
18429             _cimg_mp_scalar1(mp_bitwise_not,arg1);
18430           }
18431         }
18432 
18433         for (s = se3, ns = se2; s>ss; --s, --ns)
18434           if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^')
18435             _cimg_mp_op("Operator '^^'");
18436             arg1 = compile(ss,s,depth1,0,is_critical);
18437             arg2 = compile(s + 2,se,depth1,0,is_critical);
18438             _cimg_mp_check_type(arg1,1,3,2);
18439             _cimg_mp_check_type(arg2,2,3,2);
18440             if (arg2==1) _cimg_mp_return(arg1);
18441             pos = vector(2);
18442             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
18443               CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code);
18444               _cimg_mp_return(pos);
18445             }
18446             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
18447               CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code);
18448               _cimg_mp_return(pos);
18449             }
18450             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
18451               CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code);
18452               _cimg_mp_return(pos);
18453             }
18454             CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code);
18455             _cimg_mp_return(pos);
18456           }
18457 
18458         for (s = se2; s>ss; --s)
18459           if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^')
18460             _cimg_mp_op("Operator '^'");
18461             arg1 = compile(ss,s,depth1,0,is_critical);
18462             arg2 = compile(s + 1,se,depth1,0,is_critical);
18463             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
18464             if (arg2==1) _cimg_mp_return(arg1);
18465             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2);
18466             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2);
18467             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2);
18468             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18469               _cimg_mp_constant(std::pow(mem[arg1],mem[arg2]));
18470             switch (arg2) {
18471             case 0 : _cimg_mp_return(1);
18472             case 2 : _cimg_mp_scalar1(mp_sqr,arg1);
18473             case 3 : _cimg_mp_scalar1(mp_pow3,arg1);
18474             case 4 : _cimg_mp_scalar1(mp_pow4,arg1);
18475             default :
18476               if (_cimg_mp_is_constant(arg2)) {
18477                 if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); }
18478                 else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); }
18479               }
18480               _cimg_mp_scalar2(mp_pow,arg1,arg2);
18481             }
18482           }
18483 
18484         // Percentage computation.
18485         if (*se1=='%') {
18486           arg1 = compile(ss,se1,depth1,0,is_critical);
18487           arg2 = _cimg_mp_is_constant(arg1)?0:constant(100);
18488           if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
18489           if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100);
18490           _cimg_mp_scalar2(mp_div,arg1,arg2);
18491         }
18492 
18493         is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-?
18494         if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment
18495           if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) {
18496             _cimg_mp_op("Operator '++'");
18497             op = mp_self_increment;
18498           } else {
18499             _cimg_mp_op("Operator '--'");
18500             op = mp_self_decrement;
18501           }
18502           ref.assign(7);
18503           arg1 = is_sth?compile(ss2,se,depth1,ref,is_critical):
18504             compile(ss,se2,depth1,ref,is_critical); // Variable slot
18505 
18506           // Apply operator on a copy to prevent modifying a constant or a variable.
18507           if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) {
18508             if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
18509             else arg1 = scalar1(mp_copy,arg1);
18510           }
18511 
18512           if (is_sth) pos = arg1; // Determine return index, depending on pre/post action
18513           else {
18514             if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1);
18515             else pos = scalar1(mp_copy,arg1);
18516           }
18517 
18518           if (*ref==1) { // Vector value (scalar): V[k]++
18519             arg3 = ref[1]; // Vector slot
18520             arg4 = ref[2]; // Index
18521             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18522             CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code);
18523             CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
18524               move_to(code);
18525             _cimg_mp_return(pos);
18526           }
18527 
18528           if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++
18529             if (!is_critical) is_parallelizable = false;
18530             p1 = ref[1]; // Index
18531             is_relative = (bool)ref[2];
18532             arg3 = ref[3]; // Offset
18533             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18534             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18535             if (p1!=~0U) {
18536               if (!listout) _cimg_mp_return(pos);
18537               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
18538                                   arg1,p1,arg3).move_to(code);
18539             } else {
18540               if (!imgout) _cimg_mp_return(pos);
18541               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
18542                                   arg1,arg3).move_to(code);
18543             }
18544             _cimg_mp_return(pos);
18545           }
18546 
18547           if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++
18548             if (!is_critical) is_parallelizable = false;
18549             p1 = ref[1]; // Index
18550             is_relative = (bool)ref[2];
18551             arg3 = ref[3]; // X
18552             arg4 = ref[4]; // Y
18553             arg5 = ref[5]; // Z
18554             arg6 = ref[6]; // C
18555             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18556             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18557             if (p1!=~0U) {
18558               if (!listout) _cimg_mp_return(pos);
18559               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
18560                                   arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
18561             } else {
18562               if (!imgout) _cimg_mp_return(pos);
18563               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
18564                                   arg1,arg3,arg4,arg5,arg6).move_to(code);
18565             }
18566             _cimg_mp_return(pos);
18567           }
18568 
18569           if (*ref==4) { // Image value (vector): I/J[_#ind,off]++
18570             if (!is_critical) is_parallelizable = false;
18571             p1 = ref[1]; // Index
18572             is_relative = (bool)ref[2];
18573             arg3 = ref[3]; // Offset
18574             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18575             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18576             if (p1!=~0U) {
18577               if (!listout) _cimg_mp_return(pos);
18578               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
18579                                   arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
18580             } else {
18581               if (!imgout) _cimg_mp_return(pos);
18582               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
18583                                   arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
18584             }
18585             _cimg_mp_return(pos);
18586           }
18587 
18588           if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++
18589             if (!is_critical) is_parallelizable = false;
18590             p1 = ref[1]; // Index
18591             is_relative = (bool)ref[2];
18592             arg3 = ref[3]; // X
18593             arg4 = ref[4]; // Y
18594             arg5 = ref[5]; // Z
18595             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
18596             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18597             if (p1!=~0U) {
18598               if (!listout) _cimg_mp_return(pos);
18599               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
18600                                   arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
18601             } else {
18602               if (!imgout) _cimg_mp_return(pos);
18603               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
18604                                   arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
18605             }
18606             _cimg_mp_return(pos);
18607           }
18608 
18609           if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++
18610             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
18611             _cimg_mp_return(pos);
18612           }
18613 
18614           if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s++
18615             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
18616             _cimg_mp_return(pos);
18617           }
18618 
18619           if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
18620           else variable_name.assign(ss,(unsigned int)(se1 - ss));
18621           variable_name.back() = 0;
18622           cimg::strpare(variable_name,false,true);
18623           _cimg_mp_strerr;
18624           cimg::strellipsize(variable_name,64);
18625           throw CImgArgumentException("[" cimg_appname "_math_parser] "
18626                                       "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
18627                                       "in expression '%s%s%s'.",
18628                                       pixel_type(),_cimg_mp_calling_function,s_op,
18629                                       _cimg_mp_is_constant(arg1)?"const ":"",
18630                                       variable_name._data,
18631                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
18632         }
18633 
18634         // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'.
18635         if (*se1==']') {
18636           _cimg_mp_op("Value accessor '[]'");
18637 
18638           // Find opening bracket for the offset.
18639           s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
18640           if (s0>ss) { s1 = s0; do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
18641           is_sth=s0>ss && *(s0-1)==']';  // Particular case s.a. '..[..][..]' ?
18642           is_relative = *ss=='j' || *ss=='J';
18643 
18644           if (!is_sth && (*ss=='I' || *ss=='J') && *ss1=='[' &&
18645               (reserved_label[(int)*ss]==~0U ||
18646                !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector
18647             if (*ss2=='#') { // Index specified
18648               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18649               p1 = compile(ss3,s0++,depth1,0,is_critical);
18650               _cimg_mp_check_constant_index(p1);
18651               _cimg_mp_check_list(false);
18652             } else { p1 = ~0U; s0 = ss2; }
18653             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18654             p2 = 1 + (p1!=~0U);
18655             arg1 = compile(s0,s1,depth1,0,is_critical); // Offset
18656             _cimg_mp_check_type(arg1,p2,1,0);
18657             arg2 = ~0U;
18658             if (s1<se1) {
18659               arg2 = compile(++s1,se1,depth1,0,is_critical); // Boundary
18660               _cimg_mp_check_type(arg2,p2 + 1,1,0);
18661             }
18662             if (p_ref && arg2==~0U) {
18663               *p_ref = 4;
18664               p_ref[1] = p1;
18665               p_ref[2] = (unsigned int)is_relative;
18666               p_ref[3] = arg1;
18667               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
18668             }
18669             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
18670             if (p1==~0U) p2 = imgin._spectrum;
18671             else {
18672               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
18673               p2 = listin[p3]._spectrum;
18674             }
18675             if (!p2) _cimg_mp_return(0);
18676             pos = vector(p2);
18677             if (p1!=~0U) {
18678               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff),
18679                                   pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
18680             } else {
18681               need_input_copy = true;
18682               CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff),
18683                                   pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
18684             }
18685             _cimg_mp_return(pos);
18686           }
18687 
18688           if (!is_sth && (*ss=='i' || *ss=='j') && *ss1=='[' &&
18689               (reserved_label[(int)*ss]==~0U ||
18690                !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar
18691             if (*ss2=='#') { // Index specified
18692               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18693               p1 = compile(ss3,s0++,depth1,0,is_critical);
18694             } else { p1 = ~0U; s0 = ss2; }
18695             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18696             arg1 = compile(s0,s1,depth1,0,is_critical); // Offset
18697             arg2 = s1<se1?compile(++s1,se1,depth1,0,is_critical):~0U; // Boundary
18698             if (p_ref && arg2==~0U) {
18699               *p_ref = 2;
18700               p_ref[1] = p1;
18701               p_ref[2] = (unsigned int)is_relative;
18702               p_ref[3] = arg1;
18703               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
18704               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
18705             }
18706             if (p1!=~0U) {
18707               if (!listin) _cimg_mp_return(0);
18708               pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
18709             } else {
18710               if (!imgin) _cimg_mp_return(0);
18711               need_input_copy = true;
18712               pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
18713             }
18714             memtype[pos] = -2; // Prevent from being used in further optimization
18715             _cimg_mp_return(pos);
18716           }
18717 
18718           s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
18719           if (s0>ss) { // Vector element
18720             arg1 = compile(ss,s0,depth1,0,is_critical);
18721             if (_cimg_mp_is_scalar(arg1)) {
18722               variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
18723               _cimg_mp_strerr;
18724               cimg::strellipsize(variable_name,64);
18725               throw CImgArgumentException("[" cimg_appname "_math_parser] "
18726                                           "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', "
18727                                           "in expression '%s%s%s'.",
18728                                           pixel_type(),_cimg_mp_calling_function,s_op,
18729                                           variable_name._data,
18730                                           s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
18731 
18732             }
18733             s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18734 
18735             if (s1<se1) { // Two or three arguments -> sub-vector extraction
18736               p1 = _cimg_mp_size(arg1);
18737               arg2 = compile(++s0,s1,depth1,0,is_critical); // Starting index
18738               s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18739               arg3 = compile(s1,s0,depth1,0,is_critical); // Length
18740               arg4 = s0<se1?compile(++s0,se1,depth1,0,is_critical):1; // Step
18741               _cimg_mp_check_constant(arg3,2,3);
18742               arg3 = (unsigned int)mem[arg3];
18743               pos = vector(arg3);
18744               CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
18745               _cimg_mp_return(pos);
18746             }
18747 
18748             // One argument -> vector value reference
18749             arg2 = compile(++s0,se1,depth1,0,is_critical);
18750             if (_cimg_mp_is_constant(arg2)) { // Constant index
18751               nb = (int)mem[arg2];
18752               if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb);
18753               variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0;
18754               _cimg_mp_strerr;
18755               cimg::strellipsize(variable_name,64);
18756               throw CImgArgumentException("[" cimg_appname "_math_parser] "
18757                                           "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' "
18758                                           "(vector '%s' has dimension %u), "
18759                                           "in expression '%s%s%s'.",
18760                                           pixel_type(),_cimg_mp_calling_function,
18761                                           variable_name._data,nb,
18762                                           variable_name._data,_cimg_mp_size(arg1),
18763                                           s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
18764             }
18765             if (p_ref) {
18766               *p_ref = 1;
18767               p_ref[1] = arg1;
18768               p_ref[2] = arg2;
18769               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; // Prevent from being used in further optimization
18770             }
18771             pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2);
18772             memtype[pos] = -2; // Prevent from being used in further optimization
18773             _cimg_mp_return(pos);
18774           }
18775         }
18776 
18777         // Look for a function call, an access to image value, or a parenthesis.
18778         if (*se1==')') {
18779           if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_critical)); // Simple parentheses
18780           _cimg_mp_op("Value accessor '()'");
18781           is_relative = *ss=='j' || *ss=='J';
18782           s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
18783 
18784           // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions)
18785           if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar
18786             if (*ss2=='#') { // Index specified
18787               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18788               p1 = compile(ss3,s0++,depth1,0,is_critical);
18789               _cimg_mp_check_constant_index(p1);
18790               _cimg_mp_check_list(false);
18791             } else { p1 = ~0U; s0 = ss2; }
18792             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
18793             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
18794             arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
18795             arg4 = arg5 = ~0U;
18796             if (s0<se1) {
18797               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18798               arg1 = compile(s0,s1,depth1,0,is_critical);
18799               if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
18800                 p2 = _cimg_mp_size(arg1);
18801                 ++arg1;
18802                 if (p2>1) {
18803                   arg2 = arg1 + 1;
18804                   if (p2>2) arg3 = arg2 + 1;
18805                 }
18806                 if (s1<se1) {
18807                   s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18808                   arg4 = compile(s1,s2,depth1,0,is_critical);
18809                   arg5 = s2<se1?compile(++s2,se1,depth1,0,is_critical):~0U;
18810                 }
18811               } else if (s1<se1) {
18812                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18813                 arg2 = compile(s1,s2,depth1,0,is_critical);
18814                 if (s2<se1) {
18815                   s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18816                   arg3 = compile(s2,s3,depth1,0,is_critical);
18817                   if (s3<se1) {
18818                     s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18819                     arg4 = compile(s3,s2,depth1,0,is_critical);
18820                     arg5 = s2<se1?compile(++s2,se1,depth1,0,is_critical):~0U;
18821                   }
18822                 }
18823               }
18824             }
18825             if (p_ref && arg4==~0U && arg5==~0U) {
18826               *p_ref = 5;
18827               p_ref[1] = p1;
18828               p_ref[2] = (unsigned int)is_relative;
18829               p_ref[3] = arg1;
18830               p_ref[4] = arg2;
18831               p_ref[5] = arg3;
18832               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
18833               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
18834               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
18835               if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
18836             }
18837             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
18838             if (p1==~0U) p2 = imgin._spectrum;
18839             else if (_cimg_mp_is_constant(p1)) {
18840               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
18841               p2 = listin[p3]._spectrum;
18842             }
18843             if (!p2) _cimg_mp_return(0);
18844             pos = vector(p2);
18845             if (p1!=~0U)
18846               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz),
18847                                    pos,p1,arg1,arg2,arg3,
18848                                    arg4==~0U?_cimg_mp_interpolation:arg4,
18849                                    arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
18850             else {
18851               need_input_copy = true;
18852               CImg<ulongT>::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz),
18853                                   pos,arg1,arg2,arg3,
18854                                   arg4==~0U?_cimg_mp_interpolation:arg4,
18855                                   arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
18856             }
18857             _cimg_mp_return(pos);
18858           }
18859 
18860           // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)
18861           if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar
18862             if (*ss2=='#') { // Index specified
18863               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18864               p1 = compile(ss3,s0++,depth1,0,is_critical);
18865             } else { p1 = ~0U; s0 = ss2; }
18866             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
18867             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
18868             arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
18869             arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
18870             arg5 = arg6 = ~0U;
18871             if (s0<se1) {
18872               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18873               arg1 = compile(s0,s1,depth1,0,is_critical);
18874               if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
18875                 p2 = _cimg_mp_size(arg1);
18876                 ++arg1;
18877                 if (p2>1) {
18878                   arg2 = arg1 + 1;
18879                   if (p2>2) {
18880                     arg3 = arg2 + 1;
18881                     if (p2>3) arg4 = arg3 + 1;
18882                   }
18883                 }
18884                 if (s1<se1) {
18885                   s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18886                   arg5 = compile(s1,s2,depth1,0,is_critical);
18887                   arg6 = s2<se1?compile(++s2,se1,depth1,0,is_critical):~0U;
18888                 }
18889               } else if (s1<se1) {
18890                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18891                 arg2 = compile(s1,s2,depth1,0,is_critical);
18892                 if (s2<se1) {
18893                   s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18894                   arg3 = compile(s2,s3,depth1,0,is_critical);
18895                   if (s3<se1) {
18896                     s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18897                     arg4 = compile(s3,s2,depth1,0,is_critical);
18898                     if (s2<se1) {
18899                       s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18900                       arg5 = compile(s2,s3,depth1,0,is_critical);
18901                       arg6 = s3<se1?compile(++s3,se1,depth1,0,is_critical):~0U;
18902                     }
18903                   }
18904                 }
18905               }
18906             }
18907             if (p_ref && arg5==~0U && arg6==~0U) {
18908               *p_ref = 3;
18909               p_ref[1] = p1;
18910               p_ref[2] = (unsigned int)is_relative;
18911               p_ref[3] = arg1;
18912               p_ref[4] = arg2;
18913               p_ref[5] = arg3;
18914               p_ref[6] = arg4;
18915               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
18916               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
18917               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
18918               if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
18919               if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2;
18920             }
18921 
18922             if (p1!=~0U) {
18923               if (!listin) _cimg_mp_return(0);
18924               pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc,
18925                             p1,arg1,arg2,arg3,arg4,
18926                             arg5==~0U?_cimg_mp_interpolation:arg5,
18927                             arg6==~0U?_cimg_mp_boundary:arg6);
18928             } else {
18929               if (!imgin) _cimg_mp_return(0);
18930               need_input_copy = true;
18931               pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc,
18932                             arg1,arg2,arg3,arg4,
18933                             arg5==~0U?_cimg_mp_interpolation:arg5,
18934                             arg6==~0U?_cimg_mp_boundary:arg6);
18935             }
18936             memtype[pos] = -2; // Prevent from being used in further optimization
18937             _cimg_mp_return(pos);
18938           }
18939 
18940           // Mathematical functions.
18941           switch (*ss) {
18942 
18943           case '_' :
18944             if (*ss1=='(') // Skip arguments
18945               _cimg_mp_return_nan();
18946             break;
18947 
18948           case 'a' :
18949             if (!std::strncmp(ss,"abs(",4)) { // Absolute value
18950               _cimg_mp_op("Function 'abs()'");
18951               arg1 = compile(ss4,se1,depth1,0,is_critical);
18952               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_abs,arg1);
18953               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::abs(mem[arg1]));
18954               _cimg_mp_scalar1(mp_abs,arg1);
18955             }
18956 
18957             if (!std::strncmp(ss,"addr(",5)) { // Pointer address
18958               _cimg_mp_op("Function 'addr()'");
18959               arg1 = compile(ss5,se1,depth1,0,is_critical);
18960               _cimg_mp_constant((double)arg1);
18961             }
18962 
18963             if (!std::strncmp(ss,"acos(",5)) { // Arccos
18964               _cimg_mp_op("Function 'acos()'");
18965               arg1 = compile(ss5,se1,depth1,0,is_critical);
18966               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acos,arg1);
18967               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::acos(mem[arg1]));
18968               _cimg_mp_scalar1(mp_acos,arg1);
18969             }
18970 
18971             if (!std::strncmp(ss,"acosh(",6)) { // Hyperbolic arccosine
18972               _cimg_mp_op("Function 'acosh()'");
18973               arg1 = compile(ss6,se1,depth1,0,is_critical);
18974               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acosh,arg1);
18975               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::acosh(mem[arg1]));
18976               _cimg_mp_scalar1(mp_acosh,arg1);
18977             }
18978 
18979             if (!std::strncmp(ss,"asinh(",6)) { // Hyperbolic arcsine
18980               _cimg_mp_op("Function 'asinh()'");
18981               arg1 = compile(ss6,se1,depth1,0,is_critical);
18982               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asinh,arg1);
18983               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::asinh(mem[arg1]));
18984               _cimg_mp_scalar1(mp_asinh,arg1);
18985             }
18986 
18987             if (!std::strncmp(ss,"atanh(",6)) { // Hyperbolic arctangent
18988               _cimg_mp_op("Function 'atanh()'");
18989               arg1 = compile(ss6,se1,depth1,0,is_critical);
18990               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atanh,arg1);
18991               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::atanh(mem[arg1]));
18992               _cimg_mp_scalar1(mp_atanh,arg1);
18993             }
18994 
18995             if (!std::strncmp(ss,"arg(",4) ||
18996                 !std::strncmp(ss,"arg0(",5) ||
18997                 !std::strncmp(ss,"arg1(",5)) { // Nth argument
18998               _cimg_mp_op(*ss3=='('?"Function 'arg()'":*ss3=='0'?"Function 'arg0()'":"Function 'arg1()'");
18999               s0 = ss4 + (*ss3!='('?1:0);
19000               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19001               arg1 = compile(s0,s1,depth1,0,is_critical);
19002               _cimg_mp_check_type(arg1,1,1,0);
19003               s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19004               arg2 = compile(s1,s2,depth1,0,is_critical);
19005               p2 = _cimg_mp_size(arg2);
19006               p3 = 3;
19007               CImg<ulongT>::vector((ulongT)(*ss3=='0'?mp_arg0:mp_arg),0,0,p2,arg1,arg2).move_to(l_opcode);
19008               for (s = ++s2; s<se; ++s) {
19009                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19010                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19011                 arg3 = compile(s,ns,depth1,0,is_critical);
19012                 _cimg_mp_check_type(arg3,p3,p2?2:1,p2);
19013                 CImg<ulongT>::vector(arg3).move_to(l_opcode);
19014                 ++p3;
19015                 s = ns;
19016               }
19017               (l_opcode>'y').move_to(opcode);
19018               opcode[2] = opcode._height;
19019               if (_cimg_mp_is_constant(arg1)) {
19020                 p3-=1; // Number of args
19021                 if (*ss3=='0') arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1] + 1);
19022                 else arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]);
19023                 if (arg1<p3) _cimg_mp_return(opcode[4 + arg1]);
19024                 if (p2) {
19025                   pos = vector(p2);
19026                   std::memset(&mem[pos] + 1,0,p2*sizeof(double));
19027                   _cimg_mp_return(pos);
19028                 } else _cimg_mp_return(0);
19029               }
19030               pos = opcode[1] = p2?vector(p2):scalar();
19031               opcode.move_to(code);
19032               _cimg_mp_return(pos);
19033             }
19034 
19035             if (!std::strncmp(ss,"asin(",5)) { // Arcsin
19036               _cimg_mp_op("Function 'asin()'");
19037               arg1 = compile(ss5,se1,depth1,0,is_critical);
19038               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1);
19039               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::asin(mem[arg1]));
19040               _cimg_mp_scalar1(mp_asin,arg1);
19041             }
19042 
19043             if (!std::strncmp(ss,"atan(",5)) { // Arctan
19044               _cimg_mp_op("Function 'atan()'");
19045               arg1 = compile(ss5,se1,depth1,0,is_critical);
19046               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1);
19047               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::atan(mem[arg1]));
19048               _cimg_mp_scalar1(mp_atan,arg1);
19049             }
19050 
19051             if (!std::strncmp(ss,"atan2(",6)) { // Arctan2
19052               _cimg_mp_op("Function 'atan2()'");
19053               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19054               arg1 = compile(ss6,s1,depth1,0,is_critical);
19055               arg2 = compile(++s1,se1,depth1,0,is_critical);
19056               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
19057               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_atan2,arg1,arg2);
19058               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_atan2,arg1,arg2);
19059               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_atan2,arg1,arg2);
19060               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
19061                 _cimg_mp_constant(std::atan2(mem[arg1],mem[arg2]));
19062               _cimg_mp_scalar2(mp_atan2,arg1,arg2);
19063             }
19064             break;
19065 
19066           case 'b' :
19067             if (!std::strncmp(ss,"break(",6)) { // Break current loop
19068               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19069                 CImg<ulongT>::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code);
19070                 _cimg_mp_return_nan();
19071               }
19072             }
19073 
19074             if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test)
19075               _cimg_mp_op("Function 'breakpoint()'");
19076               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19077                 CImg<ulongT>::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code);
19078                 _cimg_mp_return_nan();
19079               }
19080             }
19081 
19082             if (!std::strncmp(ss,"bool(",5)) { // Boolean cast
19083               _cimg_mp_op("Function 'bool()'");
19084               arg1 = compile(ss5,se1,depth1,0,is_critical);
19085               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
19086               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
19087               _cimg_mp_scalar1(mp_bool,arg1);
19088             }
19089 
19090             if (!std::strncmp(ss,"begin(",6)) { // Begin
19091               _cimg_mp_op("Function 'begin()'");
19092               code.swap(code_begin);
19093               arg1 = compile(ss6,se1,depth1,p_ref,true);
19094               code.swap(code_begin);
19095               _cimg_mp_return(arg1);
19096             }
19097 
19098             if (!std::strncmp(ss,"begin_t(",8)) { // Begin thread
19099               _cimg_mp_op("Function 'begin_t()'");
19100               code.swap(code_begin_t);
19101               arg1 = compile(ss8,se1,depth1,p_ref,true);
19102               code.swap(code_begin_t);
19103               _cimg_mp_return(arg1);
19104             }
19105             break;
19106 
19107           case 'c' :
19108             if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value
19109               _cimg_mp_op("Function 'cabs()'");
19110               arg1 = compile(ss5,se1,depth1,0,is_critical);
19111               _cimg_mp_check_type(arg1,0,3,2);
19112               if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_complex_abs,arg1,0);
19113               _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2);
19114             }
19115 
19116             if (!std::strncmp(ss,"carg(",5)) { // Complex argument
19117               _cimg_mp_op("Function 'carg()'");
19118               arg1 = compile(ss5,se1,depth1,0,is_critical);
19119               _cimg_mp_check_type(arg1,0,3,2);
19120               if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_atan2,0,arg1);
19121               _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1);
19122             }
19123 
19124             if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root
19125               _cimg_mp_op("Function 'cbrt()'");
19126               arg1 = compile(ss5,se1,depth1,0,is_critical);
19127               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1);
19128               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1]));
19129               _cimg_mp_scalar1(mp_cbrt,arg1);
19130             }
19131 
19132             if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate
19133               _cimg_mp_op("Function 'cconj()'");
19134               arg1 = compile(ss6,se1,depth1,0,is_critical);
19135               _cimg_mp_check_type(arg1,0,3,2);
19136               pos = vector(2);
19137               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1,0).move_to(code);
19138               else CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1 + 1,arg1 + 2).move_to(code);
19139               _cimg_mp_return(pos);
19140             }
19141 
19142             if (!std::strncmp(ss,"ceil(",5)) { // Ceil
19143               _cimg_mp_op("Function 'ceil()'");
19144               arg1 = compile(ss5,se1,depth1,0,is_critical);
19145               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1);
19146               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1]));
19147               _cimg_mp_scalar1(mp_ceil,arg1);
19148             }
19149 
19150             if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential
19151               _cimg_mp_op("Function 'cexp()'");
19152               arg1 = compile(ss5,se1,depth1,0,is_critical);
19153               _cimg_mp_check_type(arg1,0,3,2);
19154               pos = vector(2);
19155               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1,0).move_to(code);
19156               else CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1 + 1,arg1 + 2).move_to(code);
19157               _cimg_mp_return(pos);
19158             }
19159 
19160             if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm
19161               _cimg_mp_op("Function 'clog()'");
19162               arg1 = compile(ss5,se1,depth1,0,is_critical);
19163               _cimg_mp_check_type(arg1,0,3,2);
19164               pos = vector(2);
19165               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1,0).move_to(code);
19166               else CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1 + 1,arg1 + 2).move_to(code);
19167               _cimg_mp_return(pos);
19168             }
19169 
19170             if (!std::strncmp(ss,"ccos(",5)) { // Complex cosine
19171               _cimg_mp_op("Function 'ccos()'");
19172               arg1 = compile(ss5,se1,depth1,0,is_critical);
19173               _cimg_mp_check_type(arg1,0,3,2);
19174               pos = vector(2);
19175               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_cos,pos,arg1,0).move_to(code);
19176               else CImg<ulongT>::vector((ulongT)mp_complex_cos,pos,arg1 + 1,arg1 + 2).move_to(code);
19177               _cimg_mp_return(pos);
19178             }
19179 
19180             if (!std::strncmp(ss,"csin(",5)) { // Complex sine
19181               _cimg_mp_op("Function 'csin()'");
19182               arg1 = compile(ss5,se1,depth1,0,is_critical);
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_sin,pos,arg1,0).move_to(code);
19186               else CImg<ulongT>::vector((ulongT)mp_complex_sin,pos,arg1 + 1,arg1 + 2).move_to(code);
19187               _cimg_mp_return(pos);
19188             }
19189 
19190             if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent
19191               _cimg_mp_op("Function 'ctan()'");
19192               arg1 = compile(ss5,se1,depth1,0,is_critical);
19193               _cimg_mp_check_type(arg1,0,3,2);
19194               pos = vector(2);
19195               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_tan,pos,arg1,0).move_to(code);
19196               else CImg<ulongT>::vector((ulongT)mp_complex_tan,pos,arg1 + 1,arg1 + 2).move_to(code);
19197               _cimg_mp_return(pos);
19198             }
19199 
19200             if (!std::strncmp(ss,"ccosh(",6)) { // Complex hyperbolic cosine
19201               _cimg_mp_op("Function 'ccosh()'");
19202               arg1 = compile(ss6,se1,depth1,0,is_critical);
19203               _cimg_mp_check_type(arg1,0,3,2);
19204               pos = vector(2);
19205               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_cosh,pos,arg1,0).move_to(code);
19206               else CImg<ulongT>::vector((ulongT)mp_complex_cosh,pos,arg1 + 1,arg1 + 2).move_to(code);
19207               _cimg_mp_return(pos);
19208             }
19209 
19210             if (!std::strncmp(ss,"csinh(",6)) { // Complex hyperbolic sine
19211               _cimg_mp_op("Function 'csinh()'");
19212               arg1 = compile(ss6,se1,depth1,0,is_critical);
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_sinh,pos,arg1,0).move_to(code);
19216               else CImg<ulongT>::vector((ulongT)mp_complex_sinh,pos,arg1 + 1,arg1 + 2).move_to(code);
19217               _cimg_mp_return(pos);
19218             }
19219 
19220             if (!std::strncmp(ss,"ctanh(",6)) { // Complex hyperbolic tangent
19221               _cimg_mp_op("Function 'ctanh()'");
19222               arg1 = compile(ss6,se1,depth1,0,is_critical);
19223               _cimg_mp_check_type(arg1,0,3,2);
19224               pos = vector(2);
19225               if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_tanh,pos,arg1,0).move_to(code);
19226               else CImg<ulongT>::vector((ulongT)mp_complex_tanh,pos,arg1 + 1,arg1 + 2).move_to(code);
19227               _cimg_mp_return(pos);
19228             }
19229 
19230             if (!std::strncmp(ss,"continue(",9)) { // Continue loop
19231               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19232                 CImg<ulongT>::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code);
19233                 _cimg_mp_return_nan();
19234               }
19235             }
19236 
19237             if (!std::strncmp(ss,"copy(",5)) { // Memory copy
19238               _cimg_mp_op("Function 'copy()'");
19239               ref.assign(14);
19240               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19241               arg1 = p1 = compile(ss5,s1,depth1,ref,is_critical);
19242               s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19243               arg2 = compile(s1,s2,depth1,ref._data + 7,is_critical);
19244               arg3 = arg4 = arg5 = ~0U; arg6 = 1;
19245               if (s2<se1) {
19246                 s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
19247                 arg3 = compile(s2,s3,depth1,0,is_critical);
19248                 if (s3<se1) {
19249                   s1 = ++s3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19250                   arg4 = compile(s3,s1,depth1,0,is_critical);
19251                   if (s1<se1) {
19252                     s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19253                     arg5 = compile(s1,s2,depth1,0,is_critical);
19254                     arg6 = s2<se1?compile(++s2,se1,depth1,0,is_critical):1;
19255                   }
19256                 }
19257               }
19258               if (_cimg_mp_is_vector(arg1)) {
19259                 if (!ref[0]) ++arg1;
19260                 else if (ref[0]>=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]);
19261               }
19262               if (_cimg_mp_is_vector(arg2)) {
19263                 if (arg3==~0U) arg3 = constant(_cimg_mp_size(arg2));
19264                 if (!ref[7]) ++arg2;
19265                 if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]);
19266               }
19267               if (arg3==~0U) arg3 = 1;
19268               if (arg4==~0U) arg4 = 1;
19269               if (arg5==~0U) arg5 = 1;
19270               _cimg_mp_check_type(arg3,3,1,0);
19271               _cimg_mp_check_type(arg4,4,1,0);
19272               _cimg_mp_check_type(arg5,5,1,0);
19273               _cimg_mp_check_type(arg6,5,1,0);
19274               CImg<ulongT>(1,22).move_to(code);
19275               code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6);
19276               code.back().get_shared_rows(8,21).fill(ref);
19277               _cimg_mp_return(p1);
19278             }
19279 
19280             if (!std::strncmp(ss,"cos(",4)) { // Cosine
19281               _cimg_mp_op("Function 'cos()'");
19282               arg1 = compile(ss4,se1,depth1,0,is_critical);
19283               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1);
19284               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1]));
19285               _cimg_mp_scalar1(mp_cos,arg1);
19286             }
19287 
19288             if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine
19289               _cimg_mp_op("Function 'cosh()'");
19290               arg1 = compile(ss5,se1,depth1,0,is_critical);
19291               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1);
19292               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1]));
19293               _cimg_mp_scalar1(mp_cosh,arg1);
19294             }
19295 
19296             if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time)
19297               _cimg_mp_op("Function 'critical()'");
19298               p1 = code._width;
19299               arg1 = compile(ss + 9,se1,depth1,p_ref,true);
19300               CImg<ulongT>::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1);
19301               _cimg_mp_return(arg1);
19302             }
19303 
19304             if (!std::strncmp(ss,"crop(",5)) { // Image crop
19305               _cimg_mp_op("Function 'crop()'");
19306               if (*ss5=='#') { // Index specified
19307                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19308                 p1 = compile(ss6,s0++,depth1,0,is_critical);
19309                 _cimg_mp_check_list(false);
19310               } else { p1 = ~0U; s0 = ss5; need_input_copy = true; }
19311               pos = 0;
19312               is_sth = false; // Coordinates specified as a vector?
19313               if (s0<se1) for (s = s0; s<se; ++s, ++pos) {
19314                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19315                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19316                 arg1 = compile(s,ns,depth1,0,is_critical);
19317                 if (!pos && _cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
19318                   opcode = CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
19319                                                   arg1 + (ulongT)_cimg_mp_size(arg1));
19320                   opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode);
19321                   is_sth = true;
19322                 } else {
19323                   _cimg_mp_check_type(arg1,pos + 1,1,0);
19324                   CImg<ulongT>::vector(arg1).move_to(l_opcode);
19325                 }
19326                 s = ns;
19327               }
19328               (l_opcode>'y').move_to(opcode);
19329 
19330               arg1 = 0; arg2 = (p1!=~0U);
19331               switch (opcode._height) {
19332               case 0 : case 1 :
19333                 CImg<ulongT>::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
19334                 break;
19335               case 2 :
19336                 CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
19337                 arg1 = arg2 + 2;
19338                 break;
19339               case 3 :
19340                 CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode);
19341                 arg1 = arg2 + 2;
19342                 break;
19343               case 4 :
19344                 CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary).
19345                   move_to(opcode);
19346                 arg1 = arg2 + (is_sth?2:3);
19347                 break;
19348               case 5 :
19349                 CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]).
19350                   move_to(opcode);
19351                 arg1 = arg2 + (is_sth?2:3);
19352                 break;
19353               case 6 :
19354                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
19355                                     _cimg_mp_boundary).move_to(opcode);
19356                 arg1 = arg2 + (is_sth?2:4);
19357                 break;
19358               case 7 :
19359                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
19360                                     opcode[6]).move_to(opcode);
19361                 arg1 = arg2 + (is_sth?2:4);
19362                 break;
19363               case 8 :
19364                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6],
19365                                     opcode[7],_cimg_mp_boundary).move_to(opcode);
19366                 arg1 = arg2 + (is_sth?2:5);
19367                 break;
19368               case 9 :
19369                 arg1 = arg2 + (is_sth?2:5);
19370                 break;
19371               default : // Error -> too much arguments
19372                 _cimg_mp_strerr;
19373                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19374                                             "CImg<%s>::%s: %s: Too much arguments specified, "
19375                                             "in expression '%s%s%s'.",
19376                                             pixel_type(),_cimg_mp_calling_function,s_op,
19377                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19378               }
19379 
19380               _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0);
19381               _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0);
19382               _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0);
19383               _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0);
19384               if (opcode[4]!=(ulongT)~0U) {
19385                 _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3);
19386                 opcode[4] = (ulongT)mem[opcode[4]];
19387               }
19388               if (opcode[5]!=(ulongT)~0U) {
19389                 _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3);
19390                 opcode[5] = (ulongT)mem[opcode[5]];
19391               }
19392               if (opcode[6]!=(ulongT)~0U) {
19393                 _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3);
19394                 opcode[6] = (ulongT)mem[opcode[6]];
19395               }
19396               if (opcode[7]!=(ulongT)~0U) {
19397                 _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3);
19398                 opcode[7] = (ulongT)mem[opcode[7]];
19399               }
19400               _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0);
19401 
19402               if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U ||
19403                   opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) {
19404                 p2 = 0;
19405                 if (p1!=~0U) {
19406                   _cimg_mp_check_constant(p1,1,1);
19407                   p2 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
19408                 }
19409                 const CImg<T> &img = p1!=~0U?listin[p2]:imgin;
19410                 if (!img) {
19411                   _cimg_mp_strerr;
19412                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
19413                                               "CImg<%s>::%s: %s: Cannot crop empty image when "
19414                                               "some xyzc-coordinates are unspecified, in expression '%s%s%s'.",
19415                                               pixel_type(),_cimg_mp_calling_function,s_op,
19416                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19417                 }
19418                 if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width;
19419                 if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height;
19420                 if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth;
19421                 if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum;
19422               }
19423 
19424               pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7]));
19425               CImg<ulongT>::vector((ulongT)mp_crop,
19426                                   pos,p1,
19427                                   *opcode,opcode[1],opcode[2],opcode[3],
19428                                   opcode[4],opcode[5],opcode[6],opcode[7],
19429                                   opcode[8]).move_to(code);
19430               _cimg_mp_return(pos);
19431             }
19432 
19433             if (!std::strncmp(ss,"cross(",6)) { // Cross product
19434               _cimg_mp_op("Function 'cross()'");
19435               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19436               arg1 = compile(ss6,s1,depth1,0,is_critical);
19437               arg2 = compile(++s1,se1,depth1,0,is_critical);
19438               _cimg_mp_check_type(arg1,1,2,3);
19439               _cimg_mp_check_type(arg2,2,2,3);
19440               pos = vector(3);
19441               CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code);
19442               _cimg_mp_return(pos);
19443             }
19444 
19445             if (!std::strncmp(ss,"cut(",4)) { // Cut
19446               _cimg_mp_op("Function 'cut()'");
19447               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19448               arg1 = compile(ss4,s1,depth1,0,is_critical);
19449               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19450               arg2 = compile(++s1,s2,depth1,0,is_critical);
19451               arg3 = compile(++s2,se1,depth1,0,is_critical);
19452               _cimg_mp_check_type(arg2,2,1,0);
19453               _cimg_mp_check_type(arg3,3,1,0);
19454               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3);
19455               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
19456                 val = mem[arg1];
19457                 val1 = mem[arg2];
19458                 val2 = mem[arg3];
19459                 _cimg_mp_constant(val<val1?val1:val>val2?val2:val);
19460               }
19461               _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3);
19462             }
19463 
19464             if (!std::strncmp(ss,"convolve(",9) || !std::strncmp(ss,"correlate(",10)) { // Convolve & Correlate
19465               is_sth = *ss2=='n'; // is_convolve?
19466               _cimg_mp_op(is_sth?"Function 'convolve()'":"Function 'correlate()'");
19467               op = is_sth?mp_convolve:mp_correlate;
19468               const ulongT default_params[] = { (ulongT)op,0, // [0]=function, [1]=result vector
19469                                                 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA
19470                                                 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM
19471                                                 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode
19472                                                 11,11,11, // [15]=xcenter, [16]=ycenter, [17]=zcenter (default value:-1)
19473                                                 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart
19474                                                 11,11,11, // [21]=xend, [22]=yend, [23]=zend (default value: -1)
19475                                                 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride
19476                                                 1,1,1 }; // [27]=xdilation, [28]=ydilation, [29]=zdilation
19477 
19478               l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'!
19479               CImg<ulongT>(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode);
19480 
19481               arg1 = 2;
19482               for (s = std::strchr(ss,'(') + 1; s<se && arg1<l_opcode[0]._height; ++s) {
19483                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19484                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19485                 l_opcode(0,arg1++) = compile(s,ns,depth1,0,is_critical);
19486                 s = ns;
19487               }
19488               l_opcode[0].move_to(opcode);
19489 
19490               if (arg1<12 || arg1>=opcode._height) {
19491                 _cimg_mp_strerr;
19492                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19493                                             "CImg<%s>::%s: %s: %s arguments provided, in expression '%s%s%s'.",
19494                                             pixel_type(),_cimg_mp_calling_function,s_op,
19495                                             arg1<12?"Not enough":"Too much",
19496                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19497               }
19498               _cimg_mp_check_type(opcode[2],1,2,0); // A
19499               _cimg_mp_check_constant(opcode[3],2,3); // wA
19500               _cimg_mp_check_constant(opcode[4],3,3); // hA
19501               _cimg_mp_check_constant(opcode[5],4,3); // dA
19502               _cimg_mp_check_constant(opcode[6],5,3); // sA
19503               _cimg_mp_check_type(opcode[7],6,2,0); // M
19504               _cimg_mp_check_constant(opcode[8],7,3); // wM
19505               _cimg_mp_check_constant(opcode[9],8,3); // hM
19506               _cimg_mp_check_constant(opcode[10],9,3); // dM
19507               _cimg_mp_check_constant(opcode[11],10,3); // sM
19508               _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions
19509               _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized
19510               _cimg_mp_check_constant(opcode[14],13,1); // channel_mode
19511               _cimg_mp_check_type(opcode[15],14,1,0); // xcenter
19512               _cimg_mp_check_type(opcode[16],15,1,0); // ycenter
19513               _cimg_mp_check_type(opcode[17],16,1,0); // zcenter
19514               _cimg_mp_check_constant(opcode[18],17,1); // xstart
19515               _cimg_mp_check_constant(opcode[19],18,1); // ystart
19516               _cimg_mp_check_constant(opcode[20],19,1); // zstart
19517               _cimg_mp_check_constant(opcode[21],20,1); // xend
19518               _cimg_mp_check_constant(opcode[22],21,1); // yend
19519               _cimg_mp_check_constant(opcode[23],22,1); // zend
19520               _cimg_mp_check_constant(opcode[24],23,3); // xstride
19521               _cimg_mp_check_constant(opcode[25],24,3); // ystride
19522               _cimg_mp_check_constant(opcode[26],25,3); // zstride
19523               _cimg_mp_check_type(opcode[27],26,1,0); // xdilation
19524               _cimg_mp_check_type(opcode[28],27,1,0); // ydilation
19525               _cimg_mp_check_type(opcode[29],28,1,0); // zdilation
19526 
19527               const unsigned int
19528                 wA = (unsigned int)mem[opcode[3]],
19529                 hA = (unsigned int)mem[opcode[4]],
19530                 dA = (unsigned int)mem[opcode[5]],
19531                 sA = (unsigned int)mem[opcode[6]],
19532                 wM = (unsigned int)mem[opcode[8]],
19533                 hM = (unsigned int)mem[opcode[9]],
19534                 dM = (unsigned int)mem[opcode[10]],
19535                 sM = (unsigned int)mem[opcode[11]],
19536                 channel_mode = (unsigned int)mem[opcode[14]],
19537                 xstart = std::min((unsigned int)mem[opcode[18]],wA - 1),
19538                 ystart = std::min((unsigned int)mem[opcode[19]],hA - 1),
19539                 zstart = std::min((unsigned int)mem[opcode[20]],dA - 1),
19540                 xend = std::min((unsigned int)mem[opcode[21]],wA - 1),
19541                 yend = std::min((unsigned int)mem[opcode[22]],hA - 1),
19542                 zend = std::min((unsigned int)mem[opcode[23]],dA - 1);
19543 
19544               if (xstart>xend || ystart>yend || zstart>zend) {
19545                 _cimg_mp_strerr;
19546                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19547                                             "CImg<%s>::%s: %s: Invalid xyz-start/end arguments "
19548                                             "(start = (%u,%u,%u), end = (%u,%u,%u)), in expression '%s%s%s'.",
19549                                             pixel_type(),_cimg_mp_calling_function,s_op,
19550                                             xstart,ystart,zstart,xend,yend,zend,
19551                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19552               }
19553 
19554               const float
19555                 xstride = (float)mem[opcode[24]],
19556                 ystride = (float)mem[opcode[25]],
19557                 zstride = (float)mem[opcode[26]];
19558 
19559               if (xstride<=0 || ystride<=0 || zstride<=0) {
19560                 _cimg_mp_strerr;
19561                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19562                                             "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), "
19563                                             "in expression '%s%s%s'.",
19564                                             pixel_type(),_cimg_mp_calling_function,s_op,
19565                                             xstride,ystride,zstride,
19566                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
19567               }
19568 
19569               arg2 = 1 + (unsigned int)std::floor((xend - xstart)/xstride);
19570               arg3 = 1 + (unsigned int)std::floor((yend - ystart)/ystride);
19571               arg4 = 1 + (unsigned int)std::floor((zend + zstart)/zstride);
19572               arg5 = channel_mode==0?sM:channel_mode==1?std::max(sA,sM):sA*sM;
19573 
19574               opcode[1] = pos = vector(arg2*arg3*arg4*arg5);
19575               opcode[3] = (ulongT)wA;
19576               opcode[4] = (ulongT)hA;
19577               opcode[5] = (ulongT)dA;
19578               opcode[6] = (ulongT)sA;
19579               opcode[8] = (ulongT)wM;
19580               opcode[9] = (ulongT)hM;
19581               opcode[10] = (ulongT)dM;
19582               opcode[11] = (ulongT)sM;
19583               opcode[14] = (ulongT)channel_mode;
19584               opcode[18] = (ulongT)xstart;
19585               opcode[19] = (ulongT)ystart;
19586               opcode[20] = (ulongT)zstart;
19587               opcode[21] = (ulongT)xend;
19588               opcode[22] = (ulongT)yend;
19589               opcode[23] = (ulongT)zend;
19590               opcode.move_to(code);
19591               _cimg_mp_return(pos);
19592             }
19593             break;
19594 
19595           case 'd' :
19596             if (*ss1=='(') { // Image depth
19597               _cimg_mp_op("Function 'd()'");
19598               if (*ss2=='#') { // Index specified
19599                 p1 = compile(ss3,se1,depth1,0,is_critical);
19600                 _cimg_mp_check_list(false);
19601               } else { if (ss2!=se1) break; p1 = ~0U; }
19602               pos = scalar();
19603               CImg<ulongT>::vector((ulongT)mp_image_d,pos,p1).move_to(code);
19604               _cimg_mp_return(pos);
19605             }
19606 
19607             if (!std::strncmp(ss,"date(",5)) { // Current date or file date
19608               _cimg_mp_op("Function 'date()'");
19609               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19610               arg1 = ss5!=se1?compile(ss5,s1,depth1,0,is_critical):~0U;
19611               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_critical):~0U;
19612               if (arg2!=~0U) _cimg_mp_check_type(arg2,1,2,0);
19613               pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar();
19614               CImg<ulongT>::vector((ulongT)mp_date,pos,_cimg_mp_size(pos),
19615                                    arg1,arg1==~0U?~0U:_cimg_mp_size(arg1),
19616                                    arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code);
19617               _cimg_mp_return(pos);
19618             }
19619 
19620             if (!std::strncmp(ss,"debug(",6)) { // Print debug info
19621               _cimg_mp_op("Function 'debug()'");
19622               p1 = code._width;
19623               arg1 = compile(ss6,se1,depth1,p_ref,is_critical);
19624               *se1 = 0;
19625               variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
19626               cimg::strpare(variable_name,false,true);
19627               ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1),
19628                 variable_name)>'y').move_to(opcode);
19629               opcode[2] = opcode._height;
19630               opcode.move_to(code,p1);
19631               *se1 = ')';
19632               _cimg_mp_return(arg1);
19633             }
19634 
19635             if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image
19636               _cimg_mp_op("Function 'display()'");
19637               if (pexpr[se2 - expr._data]=='(') { // no arguments?
19638                 CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code);
19639                 _cimg_mp_return_nan();
19640               }
19641               if (*ss8!='#') { // Vector
19642                 s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19643                 arg1 = compile(ss8,s1,depth1,0,is_critical);
19644                 arg2 = 0; arg3 = arg4 = arg5 = 1;
19645                 if (s1<se1) {
19646                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19647                   arg2 = compile(s1 + 1,s2,depth1,0,is_critical);
19648                   if (s2<se1) {
19649                     s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
19650                     arg3 = compile(s2,s3,depth1,0,is_critical);
19651                     if (s3<se1) {
19652                       s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19653                       arg4 = compile(s3,s2,depth1,0,is_critical);
19654                       arg5 = s2<se1?compile(++s2,se1,depth1,0,is_critical):0;
19655                     }
19656                   }
19657                 }
19658                 _cimg_mp_check_type(arg2,2,1,0);
19659                 _cimg_mp_check_type(arg3,3,1,0);
19660                 _cimg_mp_check_type(arg4,4,1,0);
19661                 _cimg_mp_check_type(arg5,5,1,0);
19662 
19663                 c1 = *s1; *s1 = 0;
19664                 variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true);
19665                 cimg::strpare(variable_name,false,true);
19666                 if (_cimg_mp_is_vector(arg1))
19667                   ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0),
19668                     variable_name)>'y').move_to(opcode);
19669                 else
19670                   ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0),
19671                     variable_name)>'y').move_to(opcode);
19672                 opcode[2] = opcode._height;
19673                 opcode.move_to(code);
19674 
19675                 ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1),
19676                                        arg2,arg3,arg4,arg5),
19677                   variable_name)>'y').move_to(opcode);
19678                 opcode[2] = opcode._height;
19679                 opcode.move_to(code);
19680                 *s1 = c1;
19681                 _cimg_mp_return(arg1);
19682 
19683               } else { // Image
19684                 p1 = compile(ss8 + 1,se1,depth1,0,is_critical);
19685                 _cimg_mp_check_list(true);
19686                 CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code);
19687                 _cimg_mp_return_nan();
19688               }
19689             }
19690 
19691             if (!std::strncmp(ss,"det(",4)) { // Matrix determinant
19692               _cimg_mp_op("Function 'det()'");
19693               arg1 = compile(ss4,se1,depth1,0,is_critical);
19694               _cimg_mp_check_matrix_square(arg1,1);
19695               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
19696               _cimg_mp_scalar2(mp_det,arg1,p1);
19697             }
19698 
19699             if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix
19700               _cimg_mp_op("Function 'diag()'");
19701               CImg<ulongT>::vector((ulongT)mp_diag,0,0).move_to(l_opcode);
19702               for (s = ss5; s<se; ++s) {
19703                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19704                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19705                 arg2 = compile(s,ns,depth1,0,is_critical);
19706                 if (_cimg_mp_is_vector(arg2))
19707                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
19708                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
19709                     move_to(l_opcode);
19710                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
19711                 s = ns;
19712               }
19713               (l_opcode>'y').move_to(opcode);
19714               arg1 = opcode._height - 3;
19715               pos = vector(arg1*arg1);
19716               opcode[1] = pos;
19717               opcode[2] = opcode._height;
19718               opcode.move_to(code);
19719               _cimg_mp_return(pos);
19720             }
19721 
19722             if (!std::strncmp(ss,"dot(",4)) { // Dot product
19723               _cimg_mp_op("Function 'dot()'");
19724               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19725               arg1 = compile(ss4,s1,depth1,0,is_critical);
19726               arg2 = compile(++s1,se1,depth1,0,is_critical);
19727               if (_cimg_mp_is_vector(arg1)) {
19728                 _cimg_mp_check_type(arg2,2,2,_cimg_mp_size(arg1));
19729                 _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1));
19730               }
19731               _cimg_mp_check_type(arg2,2,1,0);
19732               _cimg_mp_scalar2(mp_mul,arg1,arg2);
19733             }
19734 
19735             if (!std::strncmp(ss,"do(",3)) { // Do..while
19736               _cimg_mp_op("Function 'do()'");
19737               s0 = *ss2=='('?ss3:ss8;
19738               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19739               arg1 = code._width;
19740               arg6 = mempos;
19741               p1 = compile(s0,s1,depth1,0,is_critical); // Body
19742               arg2 = code._width;
19743               p2 = s1<se1?compile(++s1,se1,depth1,0,is_critical):p1; // Condition
19744               _cimg_mp_check_type(p2,2,1,0);
19745               CImg<ulongT>::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1),
19746                                    p1>=arg6 && !_cimg_mp_is_constant(p1),
19747                                    p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
19748               _cimg_mp_return(p1);
19749             }
19750 
19751             if (!std::strncmp(ss,"draw(",5)) { // Draw image
19752               if (!is_critical) is_parallelizable = false;
19753               _cimg_mp_op("Function 'draw()'");
19754               if (*ss5=='#') { // Index specified
19755                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19756                 p1 = compile(ss6,s0++,depth1,0,is_critical);
19757                 _cimg_mp_check_list(true);
19758               } else { p1 = ~0U; s0 = ss5; }
19759               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19760               arg1 = compile(s0,s1,depth1,0,is_critical);
19761               arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
19762               arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
19763               arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
19764               arg5 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
19765               s0 = se1;
19766               if (s1<se1) {
19767                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19768                 arg2 = compile(++s1,s0,depth1,0,is_critical);
19769                 if (_cimg_mp_is_vector(arg2)) { // Coordinates specified as a vector
19770                   p2 = _cimg_mp_size(arg2);
19771                   ++arg2;
19772                   if (p2>1) {
19773                     arg3 = arg2 + 1;
19774                     if (p2>2) {
19775                       arg4 = arg3 + 1;
19776                       if (p2>3) arg5 = arg4 + 1;
19777                     }
19778                   }
19779                   ++s0;
19780                   is_sth = true;
19781                 } else {
19782                   if (s0<se1) {
19783                     is_sth = p1!=~0U;
19784                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19785                     arg3 = compile(++s0,s1,depth1,0,is_critical);
19786                     _cimg_mp_check_type(arg3,is_sth?4:3,1,0);
19787                     if (s1<se1) {
19788                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19789                       arg4 = compile(++s1,s0,depth1,0,is_critical);
19790                       _cimg_mp_check_type(arg4,is_sth?5:4,1,0);
19791                       if (s0<se1) {
19792                         s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19793                         arg5 = compile(++s0,s1,depth1,0,is_critical);
19794                         _cimg_mp_check_type(arg5,is_sth?6:5,1,0);
19795                         s0 = ++s1;
19796                       }
19797                     }
19798                   }
19799                   is_sth = false;
19800                 }
19801               }
19802 
19803               l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'!
19804               CImg<ulongT>::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5,
19805                                    0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode);
19806 
19807               arg2 = arg3 = arg4 = arg5 = ~0U;
19808               p2 = p1!=~0U?0:1;
19809               if (s0<se1) {
19810                 s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19811                 arg2 = compile(s0,s1,depth1,0,is_critical);
19812                 _cimg_mp_check_type(arg2,p2 + (is_sth?3:6),1,0);
19813                 if (s1<se1) {
19814                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19815                   arg3 = compile(++s1,s0,depth1,0,is_critical);
19816                   _cimg_mp_check_type(arg3,p2 + (is_sth?4:7),1,0);
19817                   if (s0<se1) {
19818                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19819                     arg4 = compile(++s0,s1,depth1,0,is_critical);
19820                     _cimg_mp_check_type(arg4,p2 + (is_sth?5:8),1,0);
19821                     if (s1<se1) {
19822                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19823                       arg5 = compile(++s1,s0,depth1,0,is_critical);
19824                       _cimg_mp_check_type(arg5,p2 + (is_sth?6:9),1,0);
19825                     }
19826                   }
19827                 }
19828               }
19829               if (s0<s1) s0 = s1;
19830 
19831               l_opcode(0,8) = (ulongT)arg2;
19832               l_opcode(0,9) = (ulongT)arg3;
19833               l_opcode(0,10) = (ulongT)arg4;
19834               l_opcode(0,11) = (ulongT)arg5;
19835 
19836               if (s0<se1) {
19837                 s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19838                 arg6 = compile(++s0,s1,depth1,0,is_critical);
19839                 _cimg_mp_check_type(arg6,0,1,0);
19840                 l_opcode(0,12) = arg6;
19841                 if (s1<se1) {
19842                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19843                   p2 = compile(++s1,s0,depth1,0,is_critical);
19844                   _cimg_mp_check_type(p2,0,2,0);
19845                   l_opcode(0,13) = p2;
19846                   l_opcode(0,14) = _cimg_mp_size(p2);
19847                   p3 = s0<se1?compile(++s0,se1,depth1,0,is_critical):1;
19848                   _cimg_mp_check_type(p3,0,1,0);
19849                   l_opcode(0,15) = p3;
19850                 }
19851               }
19852               l_opcode[0].move_to(code);
19853               _cimg_mp_return(arg1);
19854             }
19855 
19856             break;
19857 
19858           case 'e' :
19859             if (!std::strncmp(ss,"echo(",5)) { // Echo
19860               _cimg_mp_op("Function 'echo()'");
19861               CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode);
19862               for (s = ss5; s<se; ++s) {
19863                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19864                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19865                 arg1 = compile(s,ns,depth1,0,is_critical);
19866                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
19867                 s = ns;
19868               }
19869               (l_opcode>'y').move_to(opcode);
19870               opcode[2] = opcode._height;
19871               opcode.move_to(code);
19872               _cimg_mp_return_nan();
19873             }
19874 
19875             if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector
19876               _cimg_mp_op("Function 'eig()'");
19877               arg1 = compile(ss4,se1,depth1,0,is_critical);
19878               _cimg_mp_check_matrix_square(arg1,1);
19879               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
19880               pos = vector((p1 + 1)*p1);
19881               CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code);
19882               _cimg_mp_return(pos);
19883             }
19884 
19885             if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing
19886               if (!is_critical) is_parallelizable = false;
19887               _cimg_mp_op("Function 'ellipse()'");
19888               if (*ss8=='#') { // Index specified
19889                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
19890                 p1 = compile(ss + 9,s0++,depth1,0,is_critical);
19891                 _cimg_mp_check_list(true);
19892               } else { p1 = ~0U; s0 = ss8; }
19893               if (s0==se1) compile(s0,se1,depth1,0,is_critical); // 'missing' argument error
19894               CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
19895               for (s = s0; s<se; ++s) {
19896                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19897                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19898                 arg2 = compile(s,ns,depth1,0,is_critical);
19899                 if (_cimg_mp_is_vector(arg2))
19900                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
19901                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
19902                     move_to(l_opcode);
19903                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
19904                 s = ns;
19905               }
19906               (l_opcode>'y').move_to(opcode);
19907               opcode[2] = opcode._height;
19908               opcode.move_to(code);
19909               _cimg_mp_return_nan();
19910             }
19911 
19912             if (!std::strncmp(ss,"exp(",4)) { // Exponential
19913               _cimg_mp_op("Function 'exp()'");
19914               arg1 = compile(ss4,se1,depth1,0,is_critical);
19915               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1);
19916               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1]));
19917               _cimg_mp_scalar1(mp_exp,arg1);
19918             }
19919 
19920             if (!std::strncmp(ss,"expr(",5)) { // Vector from expression
19921               _cimg_mp_op("Function 'expr()'");
19922               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19923               arg1 = compile(ss5,s1,depth1,0,is_critical);
19924               _cimg_mp_check_type(arg1,1,2,0);
19925               p1 = _cimg_mp_size(arg1);
19926               arg2 = arg3 = arg4 = arg5 = 0;
19927               if (s1<se1) {
19928                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19929                 arg2 = compile(++s1,s2,depth1,0,is_critical);
19930                 _cimg_mp_check_constant(arg2,2,2);
19931                 arg2 = (unsigned int)mem[arg2];
19932                 if (arg2) arg3 = arg4 = arg5 = 1;
19933                 if (s2<se1) {
19934                   s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19935                   arg3 = compile(++s2,s1,depth1,0,is_critical);
19936                   _cimg_mp_check_constant(arg3,3,3);
19937                   arg3 = (unsigned int)mem[arg3];
19938                   if (s1<se1) {
19939                     s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19940                     arg4 = compile(++s1,s2,depth1,0,is_critical);
19941                     _cimg_mp_check_constant(arg4,4,3);
19942                     arg4 = (unsigned int)mem[arg4];
19943                     arg5 = s2<se1?compile(++s2,se1,depth1,0,is_critical):0;
19944                     _cimg_mp_check_constant(arg5,5,3);
19945                     arg5 = (unsigned int)mem[arg5];
19946                   }
19947                 }
19948               }
19949               p2 = arg2*arg3*arg4*arg5;
19950               if (p2) pos = vector(p2); else pos = scalar();
19951               CImg<ulongT>::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code);
19952               _cimg_mp_return(pos);
19953             }
19954 
19955             if (!std::strncmp(ss,"eye(",4)) { // Identity matrix
19956               _cimg_mp_op("Function 'eye()'");
19957               arg1 = compile(ss4,se1,depth1,0,is_critical);
19958               _cimg_mp_check_constant(arg1,1,3);
19959               p1 = (unsigned int)mem[arg1];
19960               pos = vector(p1*p1);
19961               CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code);
19962               _cimg_mp_return(pos);
19963             }
19964 
19965             if (!std::strncmp(ss,"end(",4)) { // End
19966               _cimg_mp_op("Function 'end()'");
19967               code.swap(code_end);
19968               compile(ss4,se1,depth1,p_ref,true);
19969               code.swap(code_end);
19970               is_end_code = true;
19971               _cimg_mp_return_nan();
19972             }
19973 
19974             if (!std::strncmp(ss,"end_t(",6)) { // End thread
19975               _cimg_mp_op("Function 'end_t()'");
19976               code.swap(code_end_t);
19977               compile(ss6,se1,depth1,p_ref,true);
19978               code.swap(code_end_t);
19979               is_end_code = true;
19980               _cimg_mp_return_nan();
19981             }
19982             break;
19983 
19984           case 'f' :
19985             if (!std::strncmp(ss,"f2ui(",5)) { // Special float->uint conversion
19986               _cimg_mp_op("Function 'f2ui()'");
19987               arg1 = compile(ss5,se1,depth1,0,is_critical);
19988               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1);
19989               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::float2uint((float)mem[arg1]));
19990               _cimg_mp_scalar1(mp_f2ui,arg1);
19991             }
19992 
19993             if (!std::strncmp(ss,"fact(",5)) { // Factorial
19994               _cimg_mp_op("Function 'fact()'");
19995               arg1 = compile(ss5,se1,depth1,0,is_critical);
19996               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1);
19997               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1]));
19998               _cimg_mp_scalar1(mp_factorial,arg1);
19999             }
20000 
20001             if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci
20002               _cimg_mp_op("Function 'fibo()'");
20003               arg1 = compile(ss5,se1,depth1,0,is_critical);
20004               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1);
20005               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1]));
20006               _cimg_mp_scalar1(mp_fibonacci,arg1);
20007             }
20008 
20009             if (!std::strncmp(ss,"find(",5)) { // Find
20010               _cimg_mp_op("Function 'find()'");
20011 
20012               // First argument: data to look at.
20013               s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20014               if (*ss5=='#') { // Index specified
20015                 p1 = compile(ss6,s0,depth1,0,is_critical);
20016                 _cimg_mp_check_list(false);
20017                 arg1 = ~0U;
20018               } else { // Vector specified
20019                 arg1 = compile(ss5,s0,depth1,0,is_critical);
20020                 _cimg_mp_check_type(arg1,1,2,0);
20021                 p1 = ~0U;
20022               }
20023 
20024               // Second argument: data to find.
20025               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20026               arg2 = compile(s0,s1,depth1,0,is_critical);
20027 
20028               // Third and fourth arguments: starting index and search direction.
20029               arg3 = _cimg_mp_slot_nan; arg4 = 1;
20030               if (s1<se1) {
20031                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20032                 arg3 = compile(++s1,s0,depth1,0,is_critical);
20033                 _cimg_mp_check_type(arg3,3,1,0);
20034                 if (s0<se1) {
20035                   arg4 = compile(++s0,se1,depth1,0,is_critical);
20036                   _cimg_mp_check_type(arg4,4,1,0);
20037                 }
20038               }
20039               if (p1!=~0U) {
20040                 if (_cimg_mp_size(arg2)>1)
20041                   _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4);
20042                 _cimg_mp_scalar4(mp_list_find,p1,arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
20043               }
20044               if (_cimg_mp_size(arg2)>1)
20045                 _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4);
20046               _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
20047             }
20048 
20049             if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop
20050               _cimg_mp_op("Function 'for()'");
20051               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20052               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20053               s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
20054               arg1 = code._width;
20055               p1 = compile(ss4,s1,depth1,0,is_critical); // Init
20056               arg2 = code._width;
20057               p2 = compile(++s1,s2,depth1,0,is_critical); // Cond
20058               arg3 = code._width;
20059               arg6 = mempos;
20060               if (s3<se1) { // Body + post
20061                 p3 = compile(s3 + 1,se1,depth1,0,is_critical); // Body
20062                 arg4 = code._width;
20063                 pos = compile(++s2,s3,depth1,0,is_critical); // Post
20064               } else {
20065                 p3 = compile(++s2,se1,depth1,0,is_critical); // Body only
20066                 arg4 = pos = code._width;
20067               }
20068               _cimg_mp_check_type(p2,2,1,0);
20069               arg5 = _cimg_mp_size(pos);
20070               CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2,
20071                                    arg4 - arg3,code._width - arg4,
20072                                    p3>=arg6 && !_cimg_mp_is_constant(p3),
20073                                    p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
20074               _cimg_mp_return(p3);
20075             }
20076 
20077             if (!std::strncmp(ss,"floor(",6)) { // Floor
20078               _cimg_mp_op("Function 'floor()'");
20079               arg1 = compile(ss6,se1,depth1,0,is_critical);
20080               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1);
20081               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1]));
20082               _cimg_mp_scalar1(mp_floor,arg1);
20083             }
20084 
20085             if (!std::strncmp(ss,"fsize(",6)) { // File size
20086               _cimg_mp_op("Function 'fsize()'");
20087               arg1 = compile(ss6,se1,depth1,0,is_critical);
20088               _cimg_mp_check_type(arg1,1,2,0);
20089               pos = scalar();
20090               CImg<ulongT>::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20091               _cimg_mp_return(pos);
20092             }
20093             break;
20094 
20095           case 'g' :
20096             if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
20097               _cimg_mp_op("Function 'gauss()'");
20098               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20099               arg1 = compile(ss6,s1,depth1,0,is_critical);
20100               arg2 = arg3 = 1;
20101               if (s1<se1) {
20102                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20103                 arg2 = compile(++s1,s2,depth1,0,is_critical);
20104                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):1;
20105               }
20106               _cimg_mp_check_type(arg2,2,1,0);
20107               _cimg_mp_check_type(arg3,3,1,0);
20108               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_gauss,arg1,arg2,arg3);
20109               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
20110                 val1 = mem[arg1];
20111                 val2 = mem[arg2];
20112                 _cimg_mp_constant(std::exp(-val1*val1/(2*val2*val2))/(mem[arg3]?std::sqrt(2*val2*val2*cimg::PI):1));
20113               }
20114               _cimg_mp_scalar3(mp_gauss,arg1,arg2,arg3);
20115             }
20116 
20117             if (!std::strncmp(ss,"gcd(",4)) { // Gcd
20118               _cimg_mp_op("Function 'gcd()'");
20119               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20120               arg1 = compile(ss4,s1,depth1,0,is_critical);
20121               arg2 = compile(++s1,se1,depth1,0,is_critical);
20122               _cimg_mp_check_type(arg1,1,1,0);
20123               _cimg_mp_check_type(arg2,2,1,0);
20124               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
20125                 _cimg_mp_constant(cimg::gcd((long)mem[arg1],(long)mem[arg2]));
20126               _cimg_mp_scalar2(mp_gcd,arg1,arg2);
20127             }
20128 
20129 #ifdef cimg_mp_func_get
20130             if (!std::strncmp(ss,"get(",4)) { // Get vector from stored variable
20131               _cimg_mp_op("Function 'get()'");
20132               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20133               arg1 = compile(ss4,s1,depth1,0,is_critical);
20134               arg2 = arg3 = 0;
20135               if (s1<se1) {
20136                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20137                 arg2 = compile(++s1,s2,depth1,0,is_critical);
20138                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):0;
20139               }
20140               _cimg_mp_check_type(arg1,1,2,0);
20141               _cimg_mp_check_constant(arg2,2,2);
20142               _cimg_mp_check_type(arg3,3,1,0);
20143               p1 = _cimg_mp_size(arg1);
20144               arg2 = (unsigned int)mem[arg2];
20145               if (arg2) pos = vector(arg2); else pos = scalar();
20146               CImg<ulongT>::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code);
20147               _cimg_mp_return(pos);
20148             }
20149 #endif
20150             break;
20151 
20152           case 'h' :
20153             if (*ss1=='(') { // Image height
20154               _cimg_mp_op("Function 'h()'");
20155               if (*ss2=='#') { // Index specified
20156                 p1 = compile(ss3,se1,depth1,0,is_critical);
20157                 _cimg_mp_check_list(false);
20158               } else { if (ss2!=se1) break; p1 = ~0U; }
20159               pos = scalar();
20160               CImg<ulongT>::vector((ulongT)mp_image_h,pos,p1).move_to(code);
20161               _cimg_mp_return(pos);
20162             }
20163             break;
20164 
20165           case 'i' :
20166             if (*ss1=='c' && *ss2=='(') { // Image median
20167               _cimg_mp_op("Function 'ic()'");
20168               if (*ss3=='#') { // Index specified
20169                 p1 = compile(ss4,se1,depth1,0,is_critical);
20170                 _cimg_mp_check_list(false);
20171               } else { if (ss3!=se1) break; p1 = ~0U; }
20172               pos = scalar();
20173               CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
20174               _cimg_mp_return(pos);
20175             }
20176 
20177             if (*ss1=='c' && *ss2=='(') { // Image median
20178               _cimg_mp_op("Function 'ic()'");
20179               if (*ss3=='#') { // Index specified
20180                 p1 = compile(ss4,se1,depth1,0,is_critical);
20181                 _cimg_mp_check_list(false);
20182               } else { if (ss3!=se1) break; p1 = ~0U; }
20183               pos = scalar();
20184               CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
20185               _cimg_mp_return(pos);
20186             }
20187 
20188             if (*ss1=='n' && *ss2=='(') { // Image norm
20189               _cimg_mp_op("Function 'in()'");
20190               if (*ss3=='#') { // Index specified
20191                 p1 = compile(ss4,se1,depth1,0,is_critical);
20192                 _cimg_mp_check_list(false);
20193               } else { if (ss3!=se1) break; p1 = ~0U; }
20194               pos = scalar();
20195               CImg<ulongT>::vector((ulongT)mp_image_norm,pos,p1).move_to(code);
20196               _cimg_mp_return(pos);
20197             }
20198 
20199             if (*ss1=='f' && *ss2=='(') { // If..then[..else.]
20200               _cimg_mp_op("Function 'if()'");
20201               s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20202               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20203               arg1 = compile(ss3,s1,depth1,0,is_critical);
20204               _cimg_mp_check_type(arg1,1,1,0);
20205               if (_cimg_mp_is_constant(arg1)) {
20206                 if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,is_critical);
20207                 else return s2<se1?compile(++s2,se1,depth1,0,is_critical):0;
20208               }
20209               p2 = code._width;
20210               arg2 = compile(++s1,s2,depth1,0,is_critical);
20211               p3 = code._width;
20212               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):
20213                 _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
20214               _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
20215               arg4 = _cimg_mp_size(arg2);
20216               if (arg4) pos = vector(arg4); else pos = scalar();
20217               CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
20218                                   p3 - p2,code._width - p3,arg4).move_to(code,p2);
20219               _cimg_mp_return(pos);
20220             }
20221 
20222             if (!std::strncmp(ss,"inrange(",8)) { // Check value range
20223               _cimg_mp_op("Function 'inrange()'");
20224               s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20225               arg1 = compile(ss8,s1,depth1,0,is_critical);
20226               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20227               arg2 = compile(++s1,s2,depth1,0,is_critical);
20228               s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20229               arg3 = compile(++s2,s1,depth1,0,is_critical);
20230               arg4 = arg5 = 1;
20231               if (s1<se1) {
20232                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20233                 arg4 = compile(++s1,s2,depth1,0,is_critical);
20234                 arg5 = s2<se1?compile(++s2,se1,depth1,0,is_critical):arg4;
20235                 _cimg_mp_check_type(arg4,4,1,0);
20236                 _cimg_mp_check_type(arg5,5,1,0);
20237               }
20238               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) &&
20239                   _cimg_mp_is_constant(arg3) && _cimg_mp_is_constant(arg4) &&
20240                   _cimg_mp_is_constant(arg5)) { // Optimize constant case
20241                 val = mem[arg1]; val1 = mem[arg2]; val2 = mem[arg3];
20242                 if (val2>=val1)
20243                   is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val<val2));
20244                 else
20245                   is_sth = (mem[arg5]?(val>=val2):(val>val2)) && (mem[arg4]?(val<=val1):(val<val1));
20246                 _cimg_mp_return(is_sth?1:0);
20247               }
20248               p1 = _cimg_mp_size(arg1);
20249               p2 = _cimg_mp_size(arg2);
20250               p3 = _cimg_mp_size(arg3);
20251               arg6 = ~0U; // Size of return value
20252               if (!p1) {
20253                 arg6 = p2?p2:p3;
20254                 if (arg6) _cimg_mp_check_type(arg3,3,3,arg6);
20255               } else {
20256                 arg6 = p1;
20257                 _cimg_mp_check_type(arg2,2,3,arg6);
20258                 _cimg_mp_check_type(arg3,3,3,arg6);
20259               }
20260               pos = arg6?vector(arg6):scalar();
20261               CImg<ulongT>::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code);
20262               _cimg_mp_return(pos);
20263             }
20264 
20265 
20266             if (!std::strncmp(ss,"int(",4)) { // Integer cast
20267               _cimg_mp_op("Function 'int()'");
20268               arg1 = compile(ss4,se1,depth1,0,is_critical);
20269               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1);
20270               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]);
20271               _cimg_mp_scalar1(mp_int,arg1);
20272             }
20273 
20274             if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inversion
20275               _cimg_mp_op("Function 'invert()'");
20276               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20277               arg1 = compile(ss7,s1,depth1,0,is_critical);
20278               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_critical):1;
20279               _cimg_mp_check_type(arg2,2,1,0);
20280               if (_cimg_mp_is_vector(arg1)) {
20281                 _cimg_mp_check_matrix_square(arg1,1);
20282                 p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
20283                 pos = vector(p1*p1);
20284                 CImg<ulongT>::vector((ulongT)mp_matrix_invert,pos,arg1,p1,arg2).move_to(code);
20285                 _cimg_mp_return(pos);
20286               }
20287               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]);
20288               _cimg_mp_scalar2(mp_div,1,arg1);
20289             }
20290 
20291             if (*ss1=='s') { // Family of 'is_?()' functions
20292 
20293               if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
20294                 _cimg_mp_op("Function 'isbool()'");
20295                 if (ss7==se1) _cimg_mp_return(0);
20296                 try { arg1 = compile(ss7,se1,depth1,0,is_critical); }
20297                 catch(CImgException&) { _cimg_mp_return(0); }
20298                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1);
20299                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.);
20300                 _cimg_mp_scalar1(mp_isbool,arg1);
20301               }
20302 
20303               if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
20304                 _cimg_mp_op("Function 'isdir()'");
20305                 arg1 = compile(ss6,se1,depth1,0,is_critical);
20306                 if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0);
20307                 pos = scalar();
20308                 CImg<ulongT>::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20309                 _cimg_mp_return(pos);
20310               }
20311 
20312               if (!std::strncmp(ss,"isfile(",7)) { // Is file?
20313                 _cimg_mp_op("Function 'isfile()'");
20314                 arg1 = compile(ss7,se1,depth1,0,is_critical);
20315                 if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0);
20316                 pos = scalar();
20317                 CImg<ulongT>::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
20318                 _cimg_mp_return(pos);
20319               }
20320 
20321               if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector?
20322                 if (ss5>=se1) _cimg_mp_return(0);
20323                 _cimg_mp_op("Function 'isin()'");
20324                 pos = scalar();
20325                 CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(l_opcode);
20326                 for (s = ss5; s<se; ++s) {
20327                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20328                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20329                   arg1 = compile(s,ns,depth1,0,is_critical);
20330                   if (_cimg_mp_is_vector(arg1))
20331                     CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
20332                                            arg1 + (ulongT)_cimg_mp_size(arg1)).
20333                       move_to(l_opcode);
20334                   else CImg<ulongT>::vector(arg1).move_to(l_opcode);
20335                   s = ns;
20336                 }
20337                 (l_opcode>'y').move_to(opcode);
20338                 opcode[2] = opcode._height;
20339                 opcode.move_to(code);
20340                 _cimg_mp_return(pos);
20341               }
20342 
20343               if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
20344                 _cimg_mp_op("Function 'isinf()'");
20345                 if (ss6==se1) _cimg_mp_return(0);
20346                 arg1 = compile(ss6,se1,depth1,0,is_critical);
20347                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1);
20348                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1]));
20349                 _cimg_mp_scalar1(mp_isinf,arg1);
20350               }
20351 
20352               if (!std::strncmp(ss,"isint(",6)) { // Is integer?
20353                 _cimg_mp_op("Function 'isint()'");
20354                 if (ss6==se1) _cimg_mp_return(0);
20355                 try { arg1 = compile(ss6,se1,depth1,0,is_critical); }
20356                 catch(CImgException&) { _cimg_mp_return(0); }
20357                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1);
20358                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1]));
20359                 _cimg_mp_scalar1(mp_isint,arg1);
20360               }
20361 
20362               if (!std::strncmp(ss,"isnan(",6)) { // Is NaN?
20363                 _cimg_mp_op("Function 'isnan()'");
20364                 if (ss6==se1) _cimg_mp_return(0);
20365                 arg1 = compile(ss6,se1,depth1,0,is_critical);
20366                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1);
20367                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1]));
20368                 _cimg_mp_scalar1(mp_isnan,arg1);
20369               }
20370 
20371               if (!std::strncmp(ss,"isnum(",6)) { // Is number?
20372                 _cimg_mp_op("Function 'isnum()'");
20373                 val = 0;
20374                 if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
20375                 _cimg_mp_return(0);
20376               }
20377 
20378               if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression?
20379                 _cimg_mp_op("Function 'isexpr()'");
20380                 if (ss7==se1) _cimg_mp_return(0);
20381                 try { arg1 = compile(ss7,se1,depth1,0,is_critical); }
20382                 catch (CImgException&) { _cimg_mp_return(0); }
20383                 _cimg_mp_return(1);
20384               }
20385             }
20386             break;
20387 
20388           case 'l' :
20389             if (*ss1=='(') { // Size of image list
20390               _cimg_mp_op("Function 'l()'");
20391               if (ss2!=se1) break;
20392               _cimg_mp_scalar0(mp_list_l);
20393             }
20394 
20395             if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation
20396               _cimg_mp_op("Function 'lerp()'");
20397               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20398               arg1 = compile(ss5,s1,depth1,0,is_critical);
20399               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20400               arg2 = compile(++s1,s2,depth1,0,is_critical);
20401               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):16; // Default value is 0.5
20402               _cimg_mp_check_type(arg3,3,1,0);
20403               if (_cimg_mp_is_constant(arg3)) { // Optimize constant cases
20404                 if (!arg3) _cimg_mp_return(arg1);
20405                 if (arg3==1) _cimg_mp_return(arg2);
20406                 if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) {
20407                   const double t = mem[arg3];
20408                   _cimg_mp_constant(mem[arg1]*(1-t) + mem[arg2]*t);
20409                 }
20410               }
20411               if (_cimg_mp_is_scalar(arg1)) {
20412                 _cimg_mp_check_type(arg2,2,1,0);
20413                 _cimg_mp_scalar3(mp_lerp,arg1,arg2,arg3);
20414               }
20415               p1 = _cimg_mp_size(arg1);
20416               _cimg_mp_check_type(arg2,2,2,p1);
20417               pos = vector(p1);
20418               CImg<ulongT>::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code);
20419               _cimg_mp_return(pos);
20420             }
20421 
20422             if (!std::strncmp(ss,"log(",4)) { // Natural logarithm
20423               _cimg_mp_op("Function 'log()'");
20424               arg1 = compile(ss4,se1,depth1,0,is_critical);
20425               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1);
20426               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1]));
20427               _cimg_mp_scalar1(mp_log,arg1);
20428             }
20429 
20430             if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm
20431               _cimg_mp_op("Function 'log2()'");
20432               arg1 = compile(ss5,se1,depth1,0,is_critical);
20433               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1);
20434               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1]));
20435               _cimg_mp_scalar1(mp_log2,arg1);
20436             }
20437 
20438             if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm
20439               _cimg_mp_op("Function 'log10()'");
20440               arg1 = compile(ss6,se1,depth1,0,is_critical);
20441               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1);
20442               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1]));
20443               _cimg_mp_scalar1(mp_log10,arg1);
20444             }
20445 
20446             if (!std::strncmp(ss,"lowercase(",10)) { // Lower case
20447               _cimg_mp_op("Function 'lowercase()'");
20448               arg1 = compile(ss + 10,se1,depth1,0,is_critical);
20449               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1);
20450               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1]));
20451               _cimg_mp_scalar1(mp_lowercase,arg1);
20452             }
20453             break;
20454 
20455           case 'm' :
20456             if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication
20457               _cimg_mp_op("Function 'mul()'");
20458               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20459               arg1 = compile(ss4,s1,depth1,0,is_critical);
20460               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20461               arg2 = compile(++s1,s2,depth1,0,is_critical);
20462               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):1;
20463               _cimg_mp_check_type(arg1,1,2,0);
20464               _cimg_mp_check_type(arg2,2,2,0);
20465               _cimg_mp_check_constant(arg3,3,3);
20466               p1 = _cimg_mp_size(arg1);
20467               p2 = _cimg_mp_size(arg2);
20468               p3 = (unsigned int)mem[arg3];
20469               arg5 = p2/p3;
20470               arg4 = p1/arg5;
20471               if (arg4*arg5!=p1 || arg5*p3!=p2) {
20472                 _cimg_mp_strerr;
20473                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20474                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
20475                                             "do not match with third argument 'nb_colsB=%u', "
20476                                             "in expression '%s%s%s'.",
20477                                             pixel_type(),_cimg_mp_calling_function,s_op,
20478                                             s_type(arg1)._data,s_type(arg2)._data,p3,
20479                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20480               }
20481               pos = vector(arg4*p3);
20482               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
20483               _cimg_mp_return(pos);
20484             }
20485 
20486             if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary
20487               _cimg_mp_op("Function 'mproj()'");
20488               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20489               arg1 = compile(ss6,s1,depth1,0,is_critical); // S
20490               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20491               arg2 = compile(++s1,s2,depth1,0,is_critical); // ncolS
20492               s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20493               arg3 = compile(++s2,s1,depth1,0,is_critical); // D
20494               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20495               arg4 = compile(++s1,s2,depth1,0,is_critical); // ncolD
20496               arg5 = arg6 = p3 = 0;
20497               if (s2<se1) {
20498                 s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20499                 arg5 = compile(++s2,s1,depth1,0,is_critical); // method
20500                 if (s1<se1) {
20501                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20502                   arg6 = compile(++s1,s2,depth1,0,is_critical); // max_iter
20503                   p3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):0; // method
20504                 }
20505               }
20506               _cimg_mp_check_type(arg1,1,2,0);
20507               _cimg_mp_check_constant(arg2,2,3);
20508               _cimg_mp_check_type(arg3,3,2,0);
20509               _cimg_mp_check_constant(arg4,4,3);
20510               _cimg_mp_check_type(arg5,5,1,0);
20511               _cimg_mp_check_type(arg6,6,1,0);
20512               _cimg_mp_check_type(p3,7,1,0);
20513               p1 = _cimg_mp_size(arg1);
20514               p2 = _cimg_mp_size(arg3);
20515               const unsigned int
20516                 wS = (unsigned int)mem[arg2],
20517                 wD = (unsigned int)mem[arg4],
20518                 hS = p1/wS,
20519                 hD = p2/wD;
20520 
20521               if (wS*hS!=p1) {
20522                 _cimg_mp_strerr;
20523                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20524                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
20525                                             "do not match with second argument 'nb_colsS=%u', "
20526                                             "in expression '%s%s%s'.",
20527                                             pixel_type(),_cimg_mp_calling_function,s_op,
20528                                             s_type(arg1)._data,wS,
20529                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20530               }
20531               if (wD*hD!=p2) {
20532                 _cimg_mp_strerr;
20533                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20534                                             "CImg<%s>::%s: %s: Type of third argument ('%s') "
20535                                             "do not match with fourth argument 'nb_colsD=%u', "
20536                                             "in expression '%s%s%s'.",
20537                                             pixel_type(),_cimg_mp_calling_function,s_op,
20538                                             s_type(arg3)._data,wD,
20539                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20540               }
20541               if (hS!=hD) {
20542                 _cimg_mp_strerr;
20543                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20544                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
20545                                             "do not match with third argument ('%s'), "
20546                                             "in expression '%s%s%s'.",
20547                                             pixel_type(),_cimg_mp_calling_function,s_op,
20548                                             s_type(arg1)._data,s_type(arg3)._data,
20549                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20550               }
20551               pos = vector(wS*wD);
20552               CImg<ulongT>::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code);
20553               _cimg_mp_return(pos);
20554             }
20555 
20556             if (!std::strncmp(ss,"merge(",6)) { // Merge inter-thread variables
20557               _cimg_mp_op("Function 'merge()'");
20558               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20559               pos = compile(ss6,s1,depth1,0,is_critical);
20560               arg1 = ~0U; // Merge operator (0='=',1='+',2='-',3='*',4='/',5='min',6='max')
20561               if (s1<se1) {
20562                 ++s1;
20563                 char st_op[4] = { 0 };
20564                 is_sth = false; // blank after operator?
20565                 if (cimg_sscanf(s1," %3[=+-*/minax]%c",st_op,&sep)==2 && (sep==')' || (is_sth=cimg::is_blank(sep)))) {
20566                   if (!is_sth || (is_sth && cimg_sscanf(s1," %*[=+-*/minax ]%c",&sep)==1 && sep==')')) {
20567                     cimg::strpare(st_op,' ',false,true);
20568                     if (!st_op[1]) arg1 = *st_op=='='?0:*st_op=='+'?1:*st_op=='-'?2:*st_op=='*'?3:*st_op=='/'?4:~0U;
20569                     if (*st_op=='m' && st_op[1]=='i' && st_op[2]=='n' && !st_op[3]) arg1 = 5;
20570                     if (*st_op=='m' && st_op[1]=='a' && st_op[2]=='x' && !st_op[3]) arg1 = 6;
20571                   }
20572                 }
20573               }
20574               cimg_rofY(memmerge,k) if (memmerge(0,k)==(int)pos) {
20575                 _cimg_mp_strerr;
20576                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20577                                             "CImg<%s>::%s: %s: Merge has already been requested before "
20578                                             "for specified variable "
20579                                             "in expression '%s%s%s'.",
20580                                             pixel_type(),_cimg_mp_calling_function,s_op,
20581                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20582               }
20583               if (arg1==~0U) {
20584                 _cimg_mp_strerr;
20585                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20586                                             "CImg<%s>::%s: %s: Invalid specified operator "
20587                                             "(should be one of '=,+,-,*,/,min,max'), "
20588                                             "in expression '%s%s%s'.",
20589                                             pixel_type(),_cimg_mp_calling_function,s_op,
20590                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20591               }
20592               memmerge.resize(3,memmerge._height + 1,1,1,0,0);
20593               memmerge(0,memmerge._height - 1) = (int)pos;
20594               memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos);
20595               memmerge(2,memmerge._height - 1) = (int)arg1;
20596               _cimg_mp_return(pos);
20597             }
20598             break;
20599 
20600           case 'n' :
20601 #ifdef cimg_mp_func_name
20602             if (!std::strncmp(ss,"name(",5)) { // Get image name as a string vector
20603               _cimg_mp_op("Function 'name()'");
20604               if (*ss5=='#') { // Index specified
20605                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20606                 p1 = compile(ss6,s0++,depth1,0,is_critical);
20607                 is_sth = true; // is_index_specified?
20608                 _cimg_mp_check_list(false);
20609               } else { s0 = ss5; p1 = get_mem_img_index(); is_sth = false; }
20610               arg1 = s0<se1?compile(s0,se1,depth1,0,is_critical):~0U;
20611               if (arg1!=~0U) {
20612                 _cimg_mp_check_constant(arg1,is_sth?2:1,3);
20613                 arg1 = (unsigned int)mem[arg1];
20614               } else arg1 = 1024;
20615               pos = vector(arg1);
20616               CImg<ulongT>::vector((ulongT)mp_name,pos,p1,arg1).move_to(code);
20617               _cimg_mp_return(pos);
20618             }
20619 #endif
20620 
20621             if (!std::strncmp(ss,"narg(",5)) { // Number of arguments
20622               _cimg_mp_op("Function 'narg()'");
20623               if (ss5>=se1) _cimg_mp_return(0);
20624               arg1 = 0;
20625               for (s = ss5; s<se; ++s) {
20626                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20627                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20628                 ++arg1; s = ns;
20629               }
20630               _cimg_mp_constant((double)arg1);
20631             }
20632 
20633             if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') ||
20634                 !std::strncmp(ss,"norminf(",8) || !std::strncmp(ss,"norm(",5) ||
20635                 (!std::strncmp(ss,"norm",4) && ss5<se1 && (s=std::strchr(ss5,'('))!=0)) { // Lp norm
20636               _cimg_mp_op("Function 'normP()'");
20637               if (*ss4=='(') { arg1 = 2; s = ss5; }
20638               else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; }
20639               else if (arg1==~0U) {
20640                 arg1 = compile(ss4,s++,depth1,0,is_critical);
20641                 _cimg_mp_check_constant(arg1,0,2);
20642                 arg1 = (unsigned int)mem[arg1];
20643               } else s = std::strchr(ss4,'(') + 1;
20644               pos = scalar();
20645               switch (arg1) {
20646               case 0 :
20647                 CImg<ulongT>::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break;
20648               case 1 :
20649                 CImg<ulongT>::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break;
20650               case 2 :
20651                 CImg<ulongT>::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break;
20652               case ~0U :
20653                 CImg<ulongT>::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break;
20654               default :
20655                 CImg<ulongT>::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)).
20656                   move_to(l_opcode);
20657               }
20658               for ( ; s<se; ++s) {
20659                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20660                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20661                 arg2 = compile(s,ns,depth1,0,is_critical);
20662                 if (_cimg_mp_is_vector(arg2))
20663                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
20664                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
20665                     move_to(l_opcode);
20666                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
20667                 s = ns;
20668               }
20669 
20670               (l_opcode>'y').move_to(opcode);
20671               if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1
20672                 _cimg_mp_scalar1(mp_abs,opcode[3]);
20673               opcode[2] = opcode._height;
20674               opcode.move_to(code);
20675               _cimg_mp_return(pos);
20676             }
20677             break;
20678 
20679           case 'p' :
20680             if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
20681               _cimg_mp_op("Function 'permut()'");
20682               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20683               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20684               arg1 = compile(ss7,s1,depth1,0,is_critical);
20685               arg2 = compile(++s1,s2,depth1,0,is_critical);
20686               arg3 = compile(++s2,se1,depth1,0,is_critical);
20687               _cimg_mp_check_type(arg1,1,1,0);
20688               _cimg_mp_check_type(arg2,2,1,0);
20689               _cimg_mp_check_type(arg3,3,1,0);
20690               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
20691                 _cimg_mp_constant(cimg::permutations((int)mem[arg1],(int)mem[arg2],(bool)mem[arg3]));
20692               _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3);
20693             }
20694 
20695             if (!std::strncmp(ss,"polygon(",8)) { // Polygon/line drawing
20696               if (!is_critical) is_parallelizable = false;
20697               _cimg_mp_op("Function 'polygon()'");
20698               if (*ss8=='#') { // Index specified
20699                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20700                 p1 = compile(ss + 9,s0++,depth1,0,is_critical);
20701                 _cimg_mp_check_list(true);
20702               } else { p1 = ~0U; s0 = ss8; }
20703               if (s0==se1) compile(s0,se1,depth1,0,is_critical); // 'missing' argument error
20704               CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
20705               for (s = s0; s<se; ++s) {
20706                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20707                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20708                 arg2 = compile(s,ns,depth1,0,is_critical);
20709                 if (_cimg_mp_is_vector(arg2))
20710                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
20711                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
20712                     move_to(l_opcode);
20713                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
20714                 s = ns;
20715               }
20716               (l_opcode>'y').move_to(opcode);
20717               opcode[2] = opcode._height;
20718               opcode.move_to(code);
20719               _cimg_mp_return_nan();
20720             }
20721 
20722             if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions
20723               is_sth = ss[5]=='s'; // is prints()
20724               _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
20725               s0 = is_sth?ss7:ss6;
20726               if (*s0!='#' || is_sth) { // Regular expression
20727                 for (s = s0; s<se; ++s) {
20728                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20729                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20730                   pos = compile(s,ns,depth1,p_ref,is_critical);
20731                   c1 = *ns; *ns = 0;
20732                   variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true);
20733                   cimg::strpare(variable_name,false,true);
20734                   if (_cimg_mp_is_vector(pos)) // Vector
20735                     ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0),
20736                       variable_name)>'y').move_to(opcode);
20737                   else // Scalar
20738                     ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0),
20739                       variable_name)>'y').move_to(opcode);
20740                   opcode[2] = opcode._height;
20741                   opcode.move_to(code);
20742                   *ns = c1; s = ns;
20743                 }
20744                 _cimg_mp_return(pos);
20745               } else { // Image
20746                 p1 = compile(ss7,se1,depth1,0,is_critical);
20747                 _cimg_mp_check_list(true);
20748                 CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
20749                 _cimg_mp_return_nan();
20750               }
20751             }
20752 
20753             if (!std::strncmp(ss,"pseudoinvert(",13)) { // Matrix/scalar pseudo-inversion
20754               _cimg_mp_op("Function 'pseudoinvert()'");
20755               s1 = ss + 13; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20756               arg1 = compile(ss + 13,s1,depth1,0,is_critical);
20757               arg2 = 1;
20758               arg3 = 0;
20759               if (s1<se1) {
20760                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20761                 arg2 = compile(s1,s2,depth1,0,is_critical);
20762                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):0;
20763               }
20764               _cimg_mp_check_type(arg1,1,2,0);
20765               _cimg_mp_check_constant(arg2,2,3);
20766               _cimg_mp_check_type(arg3,3,1,0);
20767               p1 = _cimg_mp_size(arg1);
20768               p2 = (unsigned int)mem[arg2];
20769               p3 = p1/p2;
20770               if (p3*p2!=p1) {
20771                 _cimg_mp_strerr;
20772                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20773                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
20774                                             "does not match with second argument 'nb_colsA=%u', "
20775                                             "in expression '%s%s%s'.",
20776                                             pixel_type(),_cimg_mp_calling_function,s_op,
20777                                             s_type(arg1)._data,p2,
20778                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20779               }
20780               pos = vector(p1);
20781               CImg<ulongT>::vector((ulongT)mp_matrix_pseudoinvert,pos,arg1,p2,p3,arg3).move_to(code);
20782               _cimg_mp_return(pos);
20783             }
20784             break;
20785 
20786           case 'r' :
20787             if (!std::strncmp(ss,"ref(",4)) { // Variable declaration
20788               _cimg_mp_op("Function 'ref()'");
20789               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20790               if (s1>=se1 || !*s1) compile(s1,s1,depth1,0,is_critical); // Will throw missing argument error
20791               arg3 = compile(ss4,s1++,depth1,p_ref,is_critical);
20792               *se1 = 0;
20793               is_sth = true;
20794               if (*s1>='0' && *s1<='9') is_sth = false;
20795               else for (ns = s1; *ns; ++ns) if (!is_varchar(*ns)) { is_sth = false; break; }
20796               if (!is_sth) {
20797                 variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0;
20798                 cimg::strellipsize(variable_name,64);
20799                 *se1 = ')';
20800                 _cimg_mp_strerr;
20801                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
20802                                             "CImg<%s>::%s: %s: Invalid specified variable name '%s', "
20803                                             "in expression '%s%s%s'.",
20804                                             pixel_type(),_cimg_mp_calling_function,s_op,
20805                                             variable_name._data,
20806                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20807 
20808               }
20809               get_variable_pos(s1,arg1,arg2);
20810               if (arg2!=~0U) reserved_label[arg2] = arg3;
20811               else if (arg1!=~0U) variable_pos[arg1] = arg3;
20812               else { // New variable
20813                 if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
20814                 variable_pos[variable_def._width] = arg3;
20815                 CImg<char>::string(s1).move_to(variable_def);
20816               }
20817               if (_cimg_mp_is_vector(arg3))
20818                 set_variable_vector(arg3); // Prevent from being used in further optimization
20819               else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
20820               *se1 = ')';
20821               _cimg_mp_return(arg3);
20822             }
20823 
20824             if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize
20825               _cimg_mp_op("Function 'resize()'");
20826               if (*ss7!='#') { // Vector
20827                 s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20828                 arg1 = compile(ss7,s1,depth1,0,is_critical);
20829                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20830                 arg2 = compile(s1,s2,depth1,0,is_critical);
20831                 arg3 = 1;
20832                 arg4 = 0;
20833                 if (s2<se1) {
20834                   s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20835                   arg3 = compile(s2,s1,depth1,0,is_critical);
20836                   arg4 = s1<se1?compile(++s1,se1,depth1,0,is_critical):0;
20837                 }
20838                 _cimg_mp_check_constant(arg2,2,3);
20839                 arg2 = (unsigned int)mem[arg2];
20840                 _cimg_mp_check_type(arg3,3,1,0);
20841                 _cimg_mp_check_type(arg4,4,1,0);
20842                 pos = vector(arg2);
20843                 CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1),
20844                                      arg3,arg4).move_to(code);
20845                 _cimg_mp_return(pos);
20846 
20847               } else { // Image
20848                 if (!is_critical) is_parallelizable = false;
20849                 s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
20850                 p1 = compile(ss8,s0++,depth1,0,is_critical);
20851                 _cimg_mp_check_list(true);
20852                 l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
20853                 CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0).
20854                   move_to(l_opcode);
20855                 pos = 0;
20856                 for (s = s0; s<se && pos<10; ++s) {
20857                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20858                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20859                   arg1 = compile(s,ns,depth1,0,is_critical);
20860                   _cimg_mp_check_type(arg1,pos + 2,1,0);
20861                   l_opcode(0,pos + 3) = arg1;
20862                   s = ns;
20863                   ++pos;
20864                 }
20865                 if (pos<1 || pos>10) {
20866                   _cimg_mp_strerr;
20867                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
20868                                               "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.",
20869                                               pixel_type(),_cimg_mp_calling_function,s_op,
20870                                               pos<1?"Missing":"Too much",
20871                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
20872                 }
20873                 l_opcode[0].move_to(code);
20874                 _cimg_mp_return_nan();
20875               }
20876             }
20877 
20878             if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse
20879               _cimg_mp_op("Function 'reverse()'");
20880               arg1 = compile(ss8,se1,depth1,0,is_critical);
20881               if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1);
20882               p1 = _cimg_mp_size(arg1);
20883               pos = vector(p1);
20884               CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code);
20885               _cimg_mp_return(pos);
20886             }
20887 
20888             if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation
20889               _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'");
20890               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
20891               arg1 = compile(ss4,s1,depth1,0,is_critical);
20892               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_critical):1;
20893               _cimg_mp_check_type(arg2,2,1,0);
20894               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
20895               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
20896                 _cimg_mp_constant(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
20897                                   cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
20898               _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
20899             }
20900 
20901             if (!std::strncmp(ss,"rot(",4)) { // 2D/3D rotation matrix
20902               _cimg_mp_op("Function 'rot()'");
20903               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20904               arg1 = compile(ss4,s1,depth1,0,is_critical);
20905               if (s1<se1) { // 3D rotation
20906                 _cimg_mp_check_type(arg1,1,3,3);
20907                 is_sth = false; // Is coordinates as vector?
20908                 if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
20909                   is_sth = true;
20910                   p2 = _cimg_mp_size(arg1);
20911                   ++arg1;
20912                   arg2 = arg3 = 0;
20913                   if (p2>1) {
20914                     arg2 = arg1 + 1;
20915                     if (p2>2) arg3 = arg2 + 1;
20916                   }
20917                   arg4 = compile(++s1,se1,depth1,0,is_critical);
20918                 } else {
20919                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20920                   arg2 = compile(++s1,s2,depth1,0,is_critical);
20921                   s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
20922                   arg3 = compile(++s2,s3,depth1,0,is_critical);
20923                   arg4 = compile(++s3,se1,depth1,0,is_critical);
20924                   _cimg_mp_check_type(arg2,2,1,0);
20925                   _cimg_mp_check_type(arg3,3,1,0);
20926                 }
20927                 _cimg_mp_check_type(arg4,is_sth?2:4,1,0);
20928                 pos = vector(9);
20929                 CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code);
20930               } else { // 2D rotation
20931                 _cimg_mp_check_type(arg1,1,1,0);
20932                 pos = vector(4);
20933                 CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code);
20934               }
20935               _cimg_mp_return(pos);
20936             }
20937 
20938             if (!std::strncmp(ss,"round(",6)) { // Value rounding
20939               _cimg_mp_op("Function 'round()'");
20940               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20941               arg1 = compile(ss6,s1,depth1,0,is_critical);
20942               arg2 = 1;
20943               arg3 = 0;
20944               if (s1<se1) {
20945                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20946                 arg2 = compile(++s1,s2,depth1,0,is_critical);
20947                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):0;
20948               }
20949               _cimg_mp_check_type(arg2,2,1,0);
20950               _cimg_mp_check_type(arg3,3,1,0);
20951               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3);
20952               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
20953                 _cimg_mp_constant(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
20954               _cimg_mp_scalar3(mp_round,arg1,arg2,arg3);
20955             }
20956 
20957 #ifdef cimg_mp_func_run
20958             if (!std::strncmp(ss,"run(",4)) { // Run external command
20959               _cimg_mp_op("Function 'run()'");
20960               if (!is_critical) is_parallelizable = false;
20961               CImg<ulongT>::vector((ulongT)mp_run,0,0).move_to(l_opcode);
20962               pos = 1;
20963               for (s = ss4; s<se; ++s) {
20964                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
20965                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
20966                 arg1 = compile(s,ns,depth1,0,is_critical);
20967                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
20968                 s = ns;
20969               }
20970               (l_opcode>'y').move_to(opcode);
20971               pos = scalar();
20972               opcode[1] = pos;
20973               opcode[2] = opcode._height;
20974               opcode.move_to(code);
20975               _cimg_mp_return(pos);
20976             }
20977 #endif
20978             break;
20979 
20980           case 's' :
20981             if (*ss1=='(') { // Image spectrum
20982               _cimg_mp_op("Function 's()'");
20983               if (*ss2=='#') { // Index specified
20984                 p1 = compile(ss3,se1,depth1,0,is_critical);
20985                 _cimg_mp_check_list(false);
20986               } else { if (ss2!=se1) break; p1 = ~0U; }
20987               pos = scalar();
20988               CImg<ulongT>::vector((ulongT)mp_image_s,pos,p1).move_to(code);
20989               _cimg_mp_return(pos);
20990             }
20991 
20992             if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values
20993               _cimg_mp_op("Function 'same()'");
20994               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
20995               arg1 = compile(ss5,s1,depth1,0,is_critical);
20996               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
20997               arg2 = compile(++s1,s2,depth1,0,is_critical);
20998               arg3 = 11;
20999               arg4 = 1;
21000               if (s2<se1) {
21001                 s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
21002                 arg3 = compile(++s2,s3,depth1,0,is_critical);
21003                 _cimg_mp_check_type(arg3,3,1,0);
21004                 arg4 = s3<se1?compile(++s3,se1,depth1,0,is_critical):1;
21005               }
21006               p1 = _cimg_mp_size(arg1);
21007               p2 = _cimg_mp_size(arg2);
21008               _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4);
21009             }
21010 
21011             if (!std::strncmp(ss,"shift(",6)) { // Shift vector
21012               _cimg_mp_op("Function 'shift()'");
21013               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21014               arg1 = compile(ss6,s1,depth1,0,is_critical);
21015               arg2 = 1; arg3 = 0;
21016               if (s1<se1) {
21017                 s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21018                 arg2 = compile(s1,s0,depth1,0,is_critical);
21019                 arg3 = s0<se1?compile(++s0,se1,depth1,0,is_critical):0;
21020               }
21021               _cimg_mp_check_type(arg1,1,2,0);
21022               _cimg_mp_check_type(arg2,2,1,0);
21023               _cimg_mp_check_type(arg3,3,1,0);
21024               p1 = _cimg_mp_size(arg1);
21025               pos = vector(p1);
21026               CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code);
21027               _cimg_mp_return(pos);
21028             }
21029 
21030             if (!std::strncmp(ss,"sign(",5)) { // Sign
21031               _cimg_mp_op("Function 'sign()'");
21032               arg1 = compile(ss5,se1,depth1,0,is_critical);
21033               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1);
21034               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1]));
21035               _cimg_mp_scalar1(mp_sign,arg1);
21036             }
21037 
21038             if (!std::strncmp(ss,"sin(",4)) { // Sine
21039               _cimg_mp_op("Function 'sin()'");
21040               arg1 = compile(ss4,se1,depth1,0,is_critical);
21041               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1);
21042               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1]));
21043               _cimg_mp_scalar1(mp_sin,arg1);
21044             }
21045 
21046             if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal
21047               _cimg_mp_op("Function 'sinc()'");
21048               arg1 = compile(ss5,se1,depth1,0,is_critical);
21049               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1);
21050               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1]));
21051               _cimg_mp_scalar1(mp_sinc,arg1);
21052             }
21053 
21054             if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine
21055               _cimg_mp_op("Function 'sinh()'");
21056               arg1 = compile(ss5,se1,depth1,0,is_critical);
21057               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1);
21058               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1]));
21059               _cimg_mp_scalar1(mp_sinh,arg1);
21060             }
21061 
21062             if (!std::strncmp(ss,"size(",5)) { // Vector size
21063               _cimg_mp_op("Function 'size()'");
21064               arg1 = compile(ss5,se1,depth1,0,is_critical);
21065               _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1));
21066             }
21067 
21068             if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system
21069               _cimg_mp_op("Function 'solve()'");
21070               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21071               arg1 = compile(ss6,s1,depth1,0,is_critical);
21072               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21073               arg2 = compile(++s1,s2,depth1,0,is_critical);
21074               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):1;
21075               _cimg_mp_check_type(arg1,1,2,0);
21076               _cimg_mp_check_type(arg2,2,2,0);
21077               _cimg_mp_check_constant(arg3,3,3);
21078               p1 = _cimg_mp_size(arg1);
21079               p2 = _cimg_mp_size(arg2);
21080               p3 = (unsigned int)mem[arg3];
21081               arg5 = p2/p3;
21082               arg4 = p1/arg5;
21083               if (arg4*arg5!=p1 || arg5*p3!=p2) {
21084                 _cimg_mp_strerr;
21085                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21086                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
21087                                             "do not match with third argument 'nb_colsB=%u', "
21088                                             "in expression '%s%s%s'.",
21089                                             pixel_type(),_cimg_mp_calling_function,s_op,
21090                                             s_type(arg1)._data,s_type(arg2)._data,p3,
21091                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21092               }
21093               pos = vector(arg4*p3);
21094               CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
21095               _cimg_mp_return(pos);
21096             }
21097 
21098             if (!std::strncmp(ss,"sort(",5)) { // Sort vector
21099               _cimg_mp_op("Function 'sort()'");
21100               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21101               arg1 = compile(ss5,s1,depth1,0,is_critical);
21102               arg2 = arg4 = 1; arg3 = ~0U;
21103               if (s1<se1) {
21104                 s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21105                 arg2 = compile(s1,s0,depth1,0,is_critical);
21106                 if (s0<se1) {
21107                   s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21108                   arg3 = compile(s0,s1,depth1,0,is_critical);
21109                   arg4 = s1<se1?compile(++s1,se1,depth1,0,is_critical):1;
21110                 }
21111               }
21112               _cimg_mp_check_type(arg1,1,2,0);
21113               _cimg_mp_check_type(arg2,2,1,0);
21114               if (arg3!=~0U) _cimg_mp_check_type(arg3,3,1,0);
21115               _cimg_mp_check_type(arg4,4,1,0);
21116               p1 = _cimg_mp_size(arg1);
21117               pos = vector(p1);
21118               CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
21119               _cimg_mp_return(pos);
21120             }
21121 
21122             if (!std::strncmp(ss,"sqr(",4)) { // Square
21123               _cimg_mp_op("Function 'sqr()'");
21124               arg1 = compile(ss4,se1,depth1,0,is_critical);
21125               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1);
21126               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1]));
21127               _cimg_mp_scalar1(mp_sqr,arg1);
21128             }
21129 
21130             if (!std::strncmp(ss,"sqrt(",5)) { // Square root
21131               _cimg_mp_op("Function 'sqrt()'");
21132               arg1 = compile(ss5,se1,depth1,0,is_critical);
21133               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1);
21134               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1]));
21135               _cimg_mp_scalar1(mp_sqrt,arg1);
21136             }
21137 
21138             if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed
21139               _cimg_mp_op("Function 'srand()'");
21140               arg1 = ss6<se1?compile(ss6,se1,depth1,0,is_critical):~0U;
21141               if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); }
21142               _cimg_mp_scalar0(mp_srand0);
21143             }
21144 
21145             if (!std::strncmp(ss,"stats(",6)) { // Image statistics
21146               _cimg_mp_op("Function 'stats()'");
21147               if (*ss6=='#') { // Index specified
21148                 p1 = compile(ss7,se1,depth1,0,is_critical);
21149                 _cimg_mp_check_list(false);
21150               } else { if (ss6!=se1) break; p1 = ~0U; }
21151               pos = vector(14);
21152               CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code);
21153               _cimg_mp_return(pos);
21154             }
21155 
21156 #ifdef cimg_mp_func_store
21157             if (!std::strncmp(ss,"store(",6)) { // Store vector to variable
21158               _cimg_mp_op("Function 'store()'");
21159               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21160               arg1 = compile(ss6,s1,depth1,0,is_critical);
21161               p1 = _cimg_mp_size(arg1);
21162               p3 = std::max(1U,p1);
21163               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21164               arg2 = compile(++s1,s2,depth1,0,is_critical);
21165               _cimg_mp_check_type(arg2,2,2,0);
21166               p2 = _cimg_mp_size(arg2);
21167               arg3 = arg5 = arg6 = 1U; arg4 = ~0U; pos = 0;
21168               if (s2<se1) {
21169                 s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21170                 arg3 = compile(++s2,s1,depth1,0,is_critical);
21171                 _cimg_mp_check_type(arg3,3,1,0);
21172                 arg4 = arg5 = arg6 = 1U;
21173                 if (s1<se1) {
21174                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21175                   arg4 = compile(++s1,s2,depth1,0,is_critical);
21176                   _cimg_mp_check_type(arg4,4,1,0);
21177                   if (s2<se1) {
21178                     s1 = s2 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21179                     arg5 = compile(++s2,s1,depth1,0,is_critical);
21180                     _cimg_mp_check_type(arg5,5,1,0);
21181                     if (s1<se1) {
21182                       s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21183                       arg6 = compile(++s1,s2,depth1,0,is_critical);
21184                       pos = s2<se1?compile(++s2,se1,depth1,0,is_critical):0;
21185                       _cimg_mp_check_type(arg6,6,1,0);
21186                       _cimg_mp_check_type(pos,7,1,0);
21187                     }
21188                   }
21189                 }
21190               }
21191               if (arg4==~0U) arg4 = constant(p3);
21192 
21193               if (_cimg_mp_is_constant(arg3) && _cimg_mp_is_constant(arg4) &&
21194                   _cimg_mp_is_constant(arg5) && _cimg_mp_is_constant(arg6)) {
21195                 const unsigned int
21196                   varg3 = (unsigned int)mem[arg3],
21197                   varg4 = (unsigned int)mem[arg4],
21198                   varg5 = (unsigned int)mem[arg5],
21199                   varg6 = (unsigned int)mem[arg6];
21200                 if (varg3*varg4*varg5*varg6>p3) {
21201                   _cimg_mp_strerr;
21202                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
21203                                               "CImg<%s>::%s: %s: Specified dimensions (%u,%u,%u,%u) "
21204                                               "are too large for vector size (%u), "
21205                                               "in expression '%s%s%s'.",
21206                                               pixel_type(),_cimg_mp_calling_function,s_op,
21207                                               varg3,varg4,varg5,varg6,p3,
21208                                               s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21209                 }
21210               }
21211               CImg<ulongT>::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg1,p1,arg2,p2,
21212                                    arg3,arg4,arg5,arg6,pos).move_to(code);
21213               _cimg_mp_return_nan();
21214             }
21215 #endif
21216 
21217             if (!std::strncmp(ss,"stov(",5)) { // String to double
21218               _cimg_mp_op("Function 'stov()'");
21219               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21220               arg1 = compile(ss5,s1,depth1,0,is_critical);
21221               arg2 = arg3 = 0;
21222               if (s1<se1) {
21223                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21224                 arg2 = compile(++s1,s2,depth1,0,is_critical);
21225                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):0;
21226               }
21227               _cimg_mp_check_type(arg2,2,1,0);
21228               _cimg_mp_check_type(arg3,3,1,0);
21229               p1 = _cimg_mp_size(arg1);
21230               pos = scalar();
21231               CImg<ulongT>::vector((ulongT)mp_stov,pos,arg1,p1,arg2,arg3).move_to(code);
21232               _cimg_mp_return(pos);
21233             }
21234 
21235             if (!std::strncmp(ss,"string(",7)) { // Construct string from list of arguments
21236               _cimg_mp_op("Function 'string()'");
21237               CImg<ulongT>::vector((ulongT)mp_string,0,0,0).move_to(l_opcode);
21238 
21239               if (*ss7=='#') { // Output vector size specified, with '#'
21240                 s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21241                 arg1 = compile(ss8,s0++,depth1,0,is_critical);
21242                 _cimg_mp_check_constant(arg1,1,3);
21243                 arg1 = (unsigned int)mem[arg1];
21244                 s = s0;
21245               } else { arg1=~0U; s = ss7; }
21246 
21247               p1 = 0;
21248               for (; s<se; ++s) {
21249                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21250                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21251                 arg2 = compile(s,ns,depth1,0,is_critical);
21252                 p2 = _cimg_mp_size(arg2);
21253                 if (p2) p1+=p2;
21254                 else {
21255                   if (_cimg_mp_is_constant(arg2)) p1+=cimg_snprintf(variable_name.assign(24),24,"%.17g",mem[arg2]);
21256                   else p1+=23;
21257                 }
21258                 CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
21259                 s = ns;
21260               }
21261               if (arg1==~0U) arg1 = p1;
21262               pos = vector(arg1,0);
21263               (l_opcode>'y').move_to(opcode);
21264               opcode[1] = pos;
21265               opcode[2] = arg1;
21266               opcode[3] = opcode._height;
21267               opcode.move_to(code);
21268               _cimg_mp_return(pos);
21269             }
21270 
21271             if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD
21272               _cimg_mp_op("Function 'svd()'");
21273               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21274               arg1 = compile(ss4,s1,depth1,0,is_critical);
21275               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_critical):1;
21276               _cimg_mp_check_type(arg1,1,2,0);
21277               _cimg_mp_check_constant(arg2,2,3);
21278               p1 = _cimg_mp_size(arg1);
21279               p2 = (unsigned int)mem[arg2];
21280               p3 = p1/p2;
21281               if (p3*p2!=p1) {
21282                 _cimg_mp_strerr;
21283                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21284                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
21285                                             "does not match with second argument 'nb_colsA=%u', "
21286                                             "in expression '%s%s%s'.",
21287                                             pixel_type(),_cimg_mp_calling_function,s_op,
21288                                             s_type(arg1)._data,p2,
21289                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21290               }
21291               pos = vector(p1 + p2 + p2*p2);
21292               CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code);
21293               _cimg_mp_return(pos);
21294             }
21295 
21296             if (!std::strncmp(ss,"swap(",5)) { // Swap values
21297               _cimg_mp_op("Function 'swap()'");
21298               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21299               ref.assign(14);
21300               arg1 = compile(ss5,s1,depth1,ref,is_critical);
21301               arg2 = compile(++s1,se1,depth1,ref._data + 7,is_critical);
21302               p1 = _cimg_mp_size(arg1);
21303               _cimg_mp_check_type(arg2,2,p1?2:1,p1);
21304               if (_cimg_mp_is_constant(arg1) || _cimg_mp_is_constant(arg2)) {
21305                 _cimg_mp_strerr;
21306                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21307                                             "CImg<%s>::%s: %s: %s argument cannot be a constant, "
21308                                             "in expression '%s%s%s'.",
21309                                             pixel_type(),_cimg_mp_calling_function,s_op,
21310                                             _cimg_mp_is_constant(arg1)?"First":"Second",
21311                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21312               }
21313               CImg<ulongT>::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code);
21314 
21315               // Write back values of linked arg1 and arg2.
21316               const unsigned int *_ref = ref;
21317               is_sth = true; // Is first argument?
21318               do {
21319                 switch (*_ref) {
21320                 case 1 : // arg1: V[k]
21321                   arg3 = _ref[1]; // Vector slot
21322                   arg4 = _ref[2]; // Index
21323                   CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
21324                     move_to(code);
21325                   break;
21326                 case 2 : // arg1: i/j[_#ind,off]
21327                   if (!is_critical) is_parallelizable = false;
21328                   p1 = _ref[1]; // Index
21329                   is_relative = (bool)_ref[2];
21330                   arg3 = _ref[3]; // Offset
21331                   if (p1!=~0U) {
21332                     if (listout)
21333                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
21334                                            arg1,p1,arg3).move_to(code);
21335                   } else {
21336                     if (imgout)
21337                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
21338                                            arg1,arg3).move_to(code);
21339                   }
21340                   break;
21341                 case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c)
21342                   if (!is_critical) is_parallelizable = false;
21343                   p1 = _ref[1]; // Index
21344                   is_relative = (bool)_ref[2];
21345                   arg3 = _ref[3]; // X
21346                   arg4 = _ref[4]; // Y
21347                   arg5 = _ref[5]; // Z
21348                   arg6 = _ref[6]; // C
21349                   if (p1!=~0U) {
21350                     if (listout)
21351                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
21352                                            arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
21353                   } else {
21354                     if (imgout)
21355                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
21356                                            arg1,arg3,arg4,arg5,arg6).move_to(code);
21357                   }
21358                   break;
21359               case 4: // arg1: I/J[_#ind,off]
21360                 if (!is_critical) is_parallelizable = false;
21361                 p1 = _ref[1]; // Index
21362                 is_relative = (bool)_ref[2];
21363                 arg3 = _ref[3]; // Offset
21364                 if (p1!=~0U) {
21365                   if (listout) {
21366                     if (_cimg_mp_is_scalar(arg1))
21367                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
21368                                            arg1,p1,arg3).move_to(code);
21369                     else {
21370                       _cimg_mp_check_constant_index(p1);
21371                       CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
21372                                            arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
21373                     }
21374                   }
21375                 } else {
21376                   if (imgout) {
21377                     if (_cimg_mp_is_scalar(arg1))
21378                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
21379                                            arg1,arg3).move_to(code);
21380                     else
21381                       CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
21382                                            arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
21383                   }
21384                 }
21385                 break;
21386                 case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c)
21387                   if (!is_critical) is_parallelizable = false;
21388                   p1 = _ref[1]; // Index
21389                   is_relative = (bool)_ref[2];
21390                   arg3 = _ref[3]; // X
21391                   arg4 = _ref[4]; // Y
21392                   arg5 = _ref[5]; // Z
21393                   if (p1!=~0U) {
21394                     if (listout) {
21395                       if (_cimg_mp_is_scalar(arg1))
21396                         CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
21397                                              arg1,p1,arg3,arg4,arg5).move_to(code);
21398                       else {
21399                         _cimg_mp_check_constant_index(p1);
21400                         CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
21401                                              arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
21402                       }
21403                     }
21404                   } else {
21405                     if (imgout) {
21406                       if (_cimg_mp_is_scalar(arg1))
21407                         CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
21408                                              arg1,arg3,arg4,arg5).move_to(code);
21409                       else
21410                         CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
21411                                              arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
21412                     }
21413                   }
21414                   break;
21415                 }
21416 
21417                 _ref+=7;
21418                 arg1 = arg2;
21419                 is_sth = !is_sth;
21420               } while (!is_sth);
21421 
21422               if (p_ref) std::memcpy(p_ref,ref,siz_ref);
21423               _cimg_mp_return(arg1);
21424             }
21425             break;
21426 
21427           case 't' :
21428             if (!std::strncmp(ss,"tan(",4)) { // Tangent
21429               _cimg_mp_op("Function 'tan()'");
21430               arg1 = compile(ss4,se1,depth1,0,is_critical);
21431               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1);
21432               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1]));
21433               _cimg_mp_scalar1(mp_tan,arg1);
21434             }
21435 
21436             if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent
21437               _cimg_mp_op("Function 'tanh()'");
21438               arg1 = compile(ss5,se1,depth1,0,is_critical);
21439               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1);
21440               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1]));
21441               _cimg_mp_scalar1(mp_tanh,arg1);
21442             }
21443 
21444             if (!std::strncmp(ss,"trace(",6)) { // Matrix trace
21445               _cimg_mp_op("Function 'trace()'");
21446               arg1 = compile(ss6,se1,depth1,0,is_critical);
21447               _cimg_mp_check_matrix_square(arg1,1);
21448               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
21449               _cimg_mp_scalar2(mp_trace,arg1,p1);
21450             }
21451 
21452             if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose
21453               _cimg_mp_op("Function 'transpose()'");
21454               s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21455               arg1 = compile(ss + 10,s1,depth1,0,is_critical);
21456               arg2 = compile(++s1,se1,depth1,0,is_critical);
21457               _cimg_mp_check_type(arg1,1,2,0);
21458               _cimg_mp_check_constant(arg2,2,3);
21459               p1 = _cimg_mp_size(arg1);
21460               p2 = (unsigned int)mem[arg2];
21461               p3 = p1/p2;
21462               if (p2*p3!=p1) {
21463                 _cimg_mp_strerr;
21464                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21465                                             "CImg<%s>::%s: %s: Size of first argument ('%s') does not match "
21466                                             "second argument 'nb_cols=%u', in expression '%s%s%s'.",
21467                                             pixel_type(),_cimg_mp_calling_function,s_op,
21468                                             s_type(arg1)._data,p2,
21469                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21470               }
21471               pos = vector(p3*p2);
21472               CImg<ulongT>::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code);
21473               _cimg_mp_return(pos);
21474             }
21475             break;
21476 
21477           case 'u' :
21478             if (*ss1=='(') { // Random value with uniform distribution
21479               _cimg_mp_op("Function 'u()'");
21480               if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1);
21481               s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21482               arg1 = compile(ss2,s1,depth1,0,is_critical);
21483               if (s1<se1) arg2 = compile(++s1,se1,depth1,0,is_critical); else { arg2 = arg1; arg1 = 0; }
21484               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
21485               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_u,arg1,arg2);
21486               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_u,arg1,arg2);
21487               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_u,arg1,arg2);
21488               _cimg_mp_scalar2(mp_u,arg1,arg2);
21489             }
21490 
21491             if (!std::strncmp(ss,"ui2f(",5)) { // Special uint->float conversion
21492               _cimg_mp_op("Function 'ui2f()'");
21493               arg1 = compile(ss5,se1,depth1,0,is_critical);
21494               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1);
21495               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::uint2float((unsigned int)mem[arg1]));
21496               _cimg_mp_scalar1(mp_ui2f,arg1);
21497             }
21498 
21499             if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
21500               _cimg_mp_op("Function 'unref()'");
21501               arg1=~0U;
21502               for (s0 = ss6; s0<se1; s0 = s1) {
21503                 if (s0>ss6 && *s0==',') ++s0;
21504                 s1 = s0; while (s1<se1 && *s1!=',') ++s1;
21505                 c1 = *s1;
21506                 if (s1>s0) {
21507                   *s1 = 0;
21508                   get_variable_pos(s0,arg1,arg2);
21509                   if (arg2!=~0U) reserved_label[arg2] = ~0U;
21510                   else if (arg1!=~0U) {
21511                     variable_def.remove(arg1);
21512                     if (arg1<variable_pos._width - 1)
21513                       std::memmove(variable_pos._data + arg1,variable_pos._data + arg1 + 1,
21514                                    sizeof(uintT)*(variable_pos._width - arg1 - 1));
21515                     --variable_pos._width;
21516                   }
21517                   *s1 = c1;
21518                 } else compile(s0,s1,depth1,0,is_critical); // Will throw a 'missing argument' exception
21519               }
21520               _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable
21521             }
21522 
21523             if (!std::strncmp(ss,"uppercase(",10)) { // Upper case
21524               _cimg_mp_op("Function 'uppercase()'");
21525               arg1 = compile(ss + 10,se1,depth1,0,is_critical);
21526               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1);
21527               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::uppercase(mem[arg1]));
21528               _cimg_mp_scalar1(mp_uppercase,arg1);
21529             }
21530             break;
21531 
21532           case 'v' :
21533             if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) ||
21534                 !std::strncmp(ss,"vector(",7) ||
21535                 (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector
21536               _cimg_mp_op("Function 'vector()'");
21537               arg2 = 0; // Number of specified values
21538               if (arg1==~0U && *ss6!='(') {
21539                 arg1 = compile(ss6,s++,depth1,0,is_critical);
21540                 _cimg_mp_check_constant(arg1,0,3);
21541                 arg1 = (unsigned int)mem[arg1];
21542               } else s = std::strchr(ss6,'(') + 1;
21543 
21544               if (arg1==~0U && *s=='#') { // Number of elements specified as first argument with '#'
21545                 s0 = ++s; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
21546                 arg1 = compile(s,s0++,depth1,0,is_critical);
21547                 _cimg_mp_check_constant(arg1,1,3);
21548                 arg1 = (unsigned int)mem[arg1];
21549                 s = s0;
21550               }
21551 
21552               if (s<se1 || arg1==~0U) for ( ; s<se; ++s) {
21553                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21554                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21555                   arg3 = compile(s,ns,depth1,0,is_critical);
21556                   if (_cimg_mp_is_vector(arg3)) {
21557                     arg4 = _cimg_mp_size(arg3);
21558                     CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode);
21559                     arg2+=arg4;
21560                   } else { CImg<ulongT>::vector(arg3).move_to(l_opcode); ++arg2; }
21561                   s = ns;
21562                 }
21563               if (arg1==~0U) arg1 = arg2;
21564               if (!arg1) _cimg_mp_return(0);
21565               pos = vector(arg1);
21566               l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
21567               (l_opcode>'y').move_to(opcode);
21568               opcode[2] = opcode._height;
21569               opcode.move_to(code);
21570               _cimg_mp_return(pos);
21571             }
21572 
21573             if (!std::strncmp(ss,"vmax(",5) || !std::strncmp(ss,"vmin(",5) ||
21574                 !std::strncmp(ss,"vmaxabs(",8) || !std::strncmp(ss,"vminabs(",8) ||
21575                 !std::strncmp(ss,"vmed(",5) || !std::strncmp(ss,"vkth(",5) ||
21576                 !std::strncmp(ss,"vsum(",5) || !std::strncmp(ss,"vavg(",5) ||
21577                 !std::strncmp(ss,"vstd(",5) || !std::strncmp(ss,"vvar(",5) ||
21578                 !std::strncmp(ss,"vprod(",6) ||
21579                 !std::strncmp(ss,"vargmin(",8) || !std::strncmp(ss,"vargmax(",8) ||
21580                 !std::strncmp(ss,"vargminabs(",11) || !std::strncmp(ss,"vargmaxabs(",11) ||
21581                 !std::strncmp(ss,"vargkth(",8)) { // Multi-argument vector functions
21582               _cimg_mp_op(ss[1]=='a'?(ss[2]=='v'?"Function 'vavg()'":
21583                                       ss[4]=='k'?"Function 'vargkth()'":
21584                                       ss[5]=='i' && ss[7]=='('?"Function 'vargmin()'":
21585                                       ss[5]=='i'?"Function vargminabs()'":
21586                                       ss[7]=='('?"Function 'vargmax()'":
21587                                       "Function 'vargmaxabs()'"):
21588                           ss[1]=='s'?(ss[2]=='u'?"Function 'vsum()'":"Function 'vstd()'"):
21589                           ss[1]=='k'?"Function 'vkth()'":
21590                           ss[1]=='p'?"Function 'vprod()'":
21591                           ss[1]=='v'?"Function 'vvar()'":
21592                           ss[2]=='i'?(ss[4]=='('?"Function 'vmin()'":
21593                                       "Function 'vminabs()'"):
21594                           ss[2]=='a'?(ss[4]=='('?"Function 'vmax()'":
21595                                       "Function 'vmaxabs()'"):
21596                           "Function 'vmed()'");
21597               op = ss[1]=='a'?(ss[2]=='v'?mp_vavg:
21598                                ss[4]=='k'?mp_vargkth:
21599                                ss[5]=='i' && ss[7]=='('?mp_vargmin:
21600                                ss[5]=='i'?mp_vargminabs:
21601                                ss[7]=='('?mp_vargmax:mp_vargmaxabs):
21602                 ss[1]=='s'?(ss[2]=='u'?mp_vsum:mp_vstd):
21603                 ss[1]=='k'?mp_vkth:
21604                 ss[1]=='p'?mp_vprod:
21605                 ss[1]=='v'?mp_vvar:
21606                 ss[2]=='i'?(ss[4]=='('?mp_vmin:mp_vminabs):
21607                 ss[2]=='a'?(ss[4]=='('?mp_vmax:mp_vmaxabs):
21608                 mp_vmedian;
21609               CImg<ulongT>::vector((ulongT)op,0,0,0).move_to(l_opcode);
21610               p1 = ~0U;
21611               p3 = 1;
21612               for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
21613                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21614                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21615                 arg2 = compile(s,ns,depth1,0,is_critical);
21616                 p2 = _cimg_mp_size(arg2);
21617                 if (p1==~0U) { if (_cimg_mp_is_vector(arg2)) p1 = p2; }
21618                 else _cimg_mp_check_type(arg2,p3,3,p1);
21619                 CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
21620                 s = ns;
21621                 ++p3;
21622               }
21623               (l_opcode>'y').move_to(opcode);
21624               if (p1==~0U) { pos = scalar(); p1 = 0; } else pos = vector(p1);
21625               opcode[1] = pos;
21626               opcode[2] = p1;
21627               opcode[3] = opcode._height;
21628               opcode.move_to(code);
21629               _cimg_mp_return(pos);
21630             }
21631 
21632             if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string
21633               _cimg_mp_op("Function 'vtos()'");
21634               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21635               arg1 = compile(ss5,s1,depth1,0,is_critical);
21636               arg2 = 0; arg3 = ~0U;
21637               if (s1<se1) {
21638                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
21639                 arg2 = compile(++s1,s2,depth1,0,is_critical);
21640                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_critical):~0U;
21641               }
21642               _cimg_mp_check_type(arg2,2,1,0);
21643               if (arg3==~0U) { // Auto-guess best output vector size
21644                 p1 = _cimg_mp_size(arg1);
21645                 p1 = p1?25*p1 - 1:24;
21646               } else {
21647                 _cimg_mp_check_constant(arg3,3,3);
21648                 p1 = (unsigned int)mem[arg3];
21649               }
21650               pos = vector(p1);
21651               CImg<ulongT>::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code);
21652               _cimg_mp_return(pos);
21653             }
21654             break;
21655 
21656           case 'w' :
21657             if (*ss1=='(') { // Image width
21658               _cimg_mp_op("Function 'w()'");
21659               if (*ss2=='#') { // Index specified
21660                 p1 = compile(ss3,se1,depth1,0,is_critical);
21661                 _cimg_mp_check_list(false);
21662               } else { if (ss2!=se1) break; p1 = ~0U; }
21663               pos = scalar();
21664               CImg<ulongT>::vector((ulongT)mp_image_w,pos,p1).move_to(code);
21665               _cimg_mp_return(pos);
21666             }
21667 
21668             if (*ss1=='h' && *ss2=='(') { // Image width*height
21669               _cimg_mp_op("Function 'wh()'");
21670               if (*ss3=='#') { // Index specified
21671                 p1 = compile(ss4,se1,depth1,0,is_critical);
21672                 _cimg_mp_check_list(false);
21673               } else { if (ss3!=se1) break; p1 = ~0U; }
21674               pos = scalar();
21675               CImg<ulongT>::vector((ulongT)mp_image_wh,pos,p1).move_to(code);
21676               _cimg_mp_return(pos);
21677             }
21678 
21679             if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth
21680               _cimg_mp_op("Function 'whd()'");
21681               if (*ss4=='#') { // Index specified
21682                 p1 = compile(ss5,se1,depth1,0,is_critical);
21683                 _cimg_mp_check_list(false);
21684               } else { if (ss4!=se1) break; p1 = ~0U; }
21685               pos = scalar();
21686               CImg<ulongT>::vector((ulongT)mp_image_whd,pos,p1).move_to(code);
21687               _cimg_mp_return(pos);
21688             }
21689 
21690             if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum
21691               _cimg_mp_op("Function 'whds()'");
21692               if (*ss5=='#') { // Index specified
21693                 p1 = compile(ss6,se1,depth1,0,is_critical);
21694                 _cimg_mp_check_list(false);
21695               } else { if (ss5!=se1) break; p1 = ~0U; }
21696               pos = scalar();
21697               CImg<ulongT>::vector((ulongT)mp_image_whds,pos,p1).move_to(code);
21698               _cimg_mp_return(pos);
21699             }
21700 
21701             if (!std::strncmp(ss,"while(",6)) { // While...do
21702               _cimg_mp_op("Function 'while()'");
21703               s0 = *ss5=='('?ss6:ss8;
21704               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21705               p1 = code._width;
21706               arg1 = compile(s0,s1,depth1,0,is_critical);
21707               p2 = code._width;
21708               arg6 = mempos;
21709               pos = compile(++s1,se1,depth1,0,is_critical);
21710               _cimg_mp_check_type(arg1,1,1,0);
21711               arg2 = _cimg_mp_size(pos);
21712               CImg<ulongT>::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2,
21713                                    pos>=arg6 && !_cimg_mp_is_constant(pos),
21714                                    arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1);
21715               _cimg_mp_return(pos);
21716             }
21717             break;
21718 
21719           case 'x' :
21720             if (!std::strncmp(ss,"xor(",4)) { // Xor
21721               _cimg_mp_op("Function 'xor()'");
21722               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
21723               arg1 = compile(ss4,s1,depth1,0,is_critical);
21724               arg2 = compile(++s1,se1,depth1,0,is_critical);
21725               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
21726               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2);
21727               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2);
21728               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2);
21729               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
21730                 _cimg_mp_constant((longT)mem[arg1] ^ (longT)mem[arg2]);
21731               _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2);
21732             }
21733             break;
21734           }
21735 
21736           if (!std::strncmp(ss,"max(",4) || !std::strncmp(ss,"min(",4) ||
21737               !std::strncmp(ss,"maxabs(",7) || !std::strncmp(ss,"minabs(",7) ||
21738               !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
21739               !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) ||
21740               !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) ||
21741               !std::strncmp(ss,"prod(",5) ||
21742               !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) ||
21743               !std::strncmp(ss,"argminabs(",10) || !std::strncmp(ss,"argmaxabs(",10) ||
21744               !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions
21745             _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'":
21746                                   ss[3]=='k'?"Function 'argkth()'":
21747                                   ss[4]=='i' && ss[6]=='('?"Function 'argmin()'":
21748                                   ss[4]=='i'?"Function argminabs()'":
21749                                   ss[6]=='('?"Function 'argmax()'":
21750                                   "Function 'argmaxabs()'"):
21751                         *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"):
21752                         *ss=='k'?"Function 'kth()'":
21753                         *ss=='p'?"Function 'prod()'":
21754                         *ss=='v'?"Function 'var()'":
21755                         ss[1]=='i'?(ss[3]=='('?"Function 'min()'":
21756                                     "Function 'minabs()'"):
21757                         ss[1]=='a'?(ss[3]=='('?"Function 'max()'":
21758                                     "Function 'maxabs()'"):
21759                         "Function 'med()'");
21760             op = *ss=='a'?(ss[1]=='v'?mp_avg:
21761                            ss[3]=='k'?mp_argkth:
21762                            ss[4]=='i' && ss[6]=='('?mp_argmin:
21763                            ss[4]=='i'?mp_argminabs:
21764                            ss[6]=='('?mp_argmax:mp_argmaxabs):
21765               *ss=='s'?(ss[1]=='u'?mp_sum:mp_std):
21766               *ss=='k'?mp_kth:
21767               *ss=='p'?mp_prod:
21768               *ss=='v'?mp_var:
21769               ss[1]=='i'?(ss[3]=='('?mp_min:mp_minabs):
21770               ss[1]=='a'?(ss[3]=='('?mp_max:mp_maxabs):
21771               mp_median;
21772             is_sth = true; // Tell if all arguments are constant
21773             pos = scalar();
21774             CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode);
21775             for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
21776               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21777                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21778               arg2 = compile(s,ns,depth1,0,is_critical);
21779               if (_cimg_mp_is_vector(arg2))
21780                 CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
21781                                        arg2 + (ulongT)_cimg_mp_size(arg2)).
21782                   move_to(l_opcode);
21783               else CImg<ulongT>::vector(arg2).move_to(l_opcode);
21784               is_sth&=_cimg_mp_is_constant(arg2);
21785               s = ns;
21786             }
21787             (l_opcode>'y').move_to(opcode);
21788             opcode[2] = opcode._height;
21789             if (is_sth) _cimg_mp_constant(op(*this));
21790             opcode.move_to(code);
21791             _cimg_mp_return(pos);
21792           }
21793 
21794           // No corresponding built-in function -> Look for a user-defined macro call.
21795           s0 = strchr(ss,'(');
21796           if (s0) {
21797             variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
21798 
21799             // Count number of specified arguments.
21800             p1 = 0;
21801             for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) {
21802               while (*s && cimg::is_blank(*s)) ++s;
21803               if (*s==')' && !p1) break;
21804               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21805                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21806             }
21807 
21808             arg3 = 0; // Number of possible name matches
21809             cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 &&
21810                                           macro_def[l].back()==(char)p1) {
21811               p2 = (unsigned int)macro_def[l].back(); // Number of required arguments
21812               CImg<charT> _expr = macro_body[l]; // Expression to be substituted
21813 
21814               p1 = 1; // Index of current parsed argument
21815               for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments
21816                 while (*s && cimg::is_blank(*s)) ++s;
21817                 if (*s==')' && p1==1) break; // Function has no arguments
21818                 if (p1>p2) { ++p1; break; }
21819                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21820                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
21821                 variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write
21822                 arg2 = 0;
21823                 cimg_forX(_expr,k) {
21824                   if (_expr[k]==(char)p1) { // Perform argument substitution
21825                     arg1 = _expr._width;
21826                     _expr.resize(arg1 + variable_name._width - 2,1,1,1,0);
21827                     std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1);
21828                     std::memcpy(_expr._data + k,variable_name,variable_name._width - 1);
21829                     k+=variable_name._width - 2;
21830                   }
21831                   ++arg2;
21832                 }
21833               }
21834 
21835               // Recompute 'pexpr' and 'level' for evaluating substituted expression.
21836               CImg<charT> _pexpr(_expr._width);
21837               ns = _pexpr._data;
21838               for (ps = _expr._data, c1 = ' '; *ps; ++ps) {
21839                 if (!cimg::is_blank(*ps)) c1 = *ps;
21840                 *(ns++) = c1;
21841               }
21842               *ns = 0;
21843 
21844               CImg<uintT> _level = get_level(_expr);
21845               expr.swap(_expr);
21846               pexpr.swap(_pexpr);
21847               level.swap(_level);
21848               s0 = user_macro;
21849               user_macro = macro_def[l];
21850               pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_critical);
21851               user_macro = s0;
21852               level.swap(_level);
21853               pexpr.swap(_pexpr);
21854               expr.swap(_expr);
21855               _cimg_mp_return(pos);
21856             }
21857 
21858             if (arg3) { // Macro name matched but number of arguments does not
21859               CImg<uintT> sig_nargs(arg3);
21860               arg1 = 0;
21861               cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name))
21862                 sig_nargs[arg1++] = (unsigned int)macro_def[l].back();
21863               _cimg_mp_strerr;
21864               cimg::strellipsize(variable_name,64);
21865               if (sig_nargs._width>1) {
21866                 sig_nargs.sort();
21867                 arg1 = sig_nargs.back();
21868                 --sig_nargs._width;
21869                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21870                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
21871                                             "does not match macro declaration (defined for %s or %u arguments), "
21872                                             "in expression '%s%s%s'.",
21873                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
21874                                             p1,sig_nargs.value_string()._data,arg1,
21875                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21876               } else
21877                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
21878                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
21879                                             "does not match macro declaration (defined for %u argument%s), "
21880                                             "in expression '%s%s%s'.",
21881                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
21882                                             p1,*sig_nargs,*sig_nargs!=1?"s":"",
21883                                             s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21884             }
21885           }
21886         } // if (se1==')')
21887 
21888         // Char / string initializer.
21889         if (*se1=='\'' &&
21890             ((se1>ss && *ss=='\'') ||
21891             (se1>ss1 && *ss=='_' && *ss1=='\''))) {
21892           if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; }
21893           else { _cimg_mp_op("String initializer"); s1 = ss1; }
21894           arg1 = (unsigned int)(se1 - s1); // Original string length
21895           if (arg1) {
21896             CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0;
21897             cimg::strunescape(variable_name);
21898             arg1 = (unsigned int)std::strlen(variable_name);
21899           }
21900           if (!arg1) _cimg_mp_return(0); // Empty string -> 0
21901           if (*ss=='_') {
21902             if (arg1==1) _cimg_mp_constant((unsigned char)*variable_name);
21903             _cimg_mp_strerr;
21904             cimg::strellipsize(variable_name,64);
21905             throw CImgArgumentException("[" cimg_appname "_math_parser] "
21906                                         "CImg<%s>::%s: %s: Literal %s contains more than one byte, "
21907                                         "in expression '%s%s%s'.",
21908                                         pixel_type(),_cimg_mp_calling_function,s_op,
21909                                         ss1,
21910                                         s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
21911           }
21912           pos = vector(arg1);
21913           CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
21914           CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
21915           std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
21916           (l_opcode>'y').move_to(code);
21917           _cimg_mp_return(pos);
21918         }
21919 
21920         // Vector initializer [ ... ].
21921         if (*ss=='[' && *se1==']') {
21922           _cimg_mp_op("Vector initializer");
21923           s1 = ss1; while (s1<se2 && cimg::is_blank(*s1)) ++s1;
21924           s2 = se2; while (s2>s1 && cimg::is_blank(*s2)) --s2;
21925           if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string
21926             arg1 = (unsigned int)(s2 - s1 - 1); // Original string length
21927             if (arg1) {
21928               CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0;
21929               cimg::strunescape(variable_name);
21930               arg1 = (unsigned int)std::strlen(variable_name);
21931             }
21932             if (!arg1) _cimg_mp_return(0); // Empty string -> 0
21933             pos = vector(arg1);
21934             CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
21935             CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
21936             std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
21937             (l_opcode>'y').move_to(code);
21938           } else { // Vector values provided as list of items
21939             arg1 = 0; // Number of specified values
21940             if (*ss1!=']') for (s = ss1; s<se; ++s) {
21941                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
21942                                (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns;
21943                 arg2 = compile(s,ns,depth1,0,is_critical);
21944                 if (_cimg_mp_is_vector(arg2)) {
21945                   arg3 = _cimg_mp_size(arg2);
21946                   CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode);
21947                   arg1+=arg3;
21948                 } else { CImg<ulongT>::vector(arg2).move_to(l_opcode); ++arg1; }
21949                 s = ns;
21950               }
21951             if (!arg1) _cimg_mp_return(0);
21952             pos = vector(arg1);
21953             l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
21954             (l_opcode>'y').move_to(opcode);
21955             opcode[2] = opcode._height;
21956             opcode.move_to(code);
21957           }
21958           _cimg_mp_return(pos);
21959         }
21960 
21961         // Variables related to the input list of images.
21962         if (*ss1=='#' && ss2<se) {
21963           arg1 = compile(ss2,se,depth1,0,is_critical);
21964           p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
21965           switch (*ss) {
21966           case 'w' : // w#ind
21967             if (!listin) _cimg_mp_return(0);
21968             if (p1!=~0U) _cimg_mp_constant(listin[p1]._width);
21969             _cimg_mp_scalar1(mp_list_width,arg1);
21970           case 'h' : // h#ind
21971             if (!listin) _cimg_mp_return(0);
21972             if (p1!=~0U) _cimg_mp_constant(listin[p1]._height);
21973             _cimg_mp_scalar1(mp_list_height,arg1);
21974           case 'd' : // d#ind
21975             if (!listin) _cimg_mp_return(0);
21976             if (p1!=~0U) _cimg_mp_constant(listin[p1]._depth);
21977             _cimg_mp_scalar1(mp_list_depth,arg1);
21978           case 'r' : // r#ind
21979             if (!listin) _cimg_mp_return(0);
21980             if (p1!=~0U) _cimg_mp_constant(listin[p1]._is_shared);
21981             _cimg_mp_scalar1(mp_list_is_shared,arg1);
21982           case 's' : // s#ind
21983             if (!listin) _cimg_mp_return(0);
21984             if (p1!=~0U) _cimg_mp_constant(listin[p1]._spectrum);
21985             _cimg_mp_scalar1(mp_list_spectrum,arg1);
21986           case 'i' : // i#ind
21987             if (!listin) _cimg_mp_return(0);
21988             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c,
21989                              0,_cimg_mp_boundary);
21990           case 'I' : // I#ind
21991             p2 = p1!=~0U?listin[p1]._spectrum:listin._width?~0U:0;
21992             if (!p2) _cimg_mp_return(0);
21993             pos = vector(p2);
21994             CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code);
21995             _cimg_mp_return(pos);
21996           case 'R' : // R#ind
21997             if (!listin) _cimg_mp_return(0);
21998             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,
21999                              0,_cimg_mp_boundary);
22000           case 'G' : // G#ind
22001             if (!listin) _cimg_mp_return(0);
22002             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,
22003                              0,_cimg_mp_boundary);
22004           case 'B' : // B#ind
22005             if (!listin) _cimg_mp_return(0);
22006             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,
22007                              0,_cimg_mp_boundary);
22008           case 'A' : // A#ind
22009             if (!listin) _cimg_mp_return(0);
22010             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,
22011                              0,_cimg_mp_boundary);
22012           }
22013         }
22014 
22015         if (*ss1 && *ss2=='#' && ss3<se) {
22016           arg1 = compile(ss3,se,depth1,0,is_critical);
22017           p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22018           if (*ss=='w' && *ss1=='h') { // wh#ind
22019             if (!listin) _cimg_mp_return(0);
22020             if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height);
22021             _cimg_mp_scalar1(mp_list_wh,arg1);
22022           }
22023           arg2 = ~0U;
22024 
22025           if (*ss=='i') {
22026             if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
22027               if (!listin) _cimg_mp_return(0);
22028               _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0',
22029                                0,_cimg_mp_boundary);
22030             }
22031 
22032             if (*ss1=='c') { // ic#ind
22033               if (!listin) _cimg_mp_return(0);
22034               if (_cimg_mp_is_constant(arg1)) {
22035                 if (!list_median) list_median.assign(listin._width);
22036                 if (!list_median[p1]) CImg<doubleT>::vector(listin[p1].median()).move_to(list_median[p1]);
22037                 _cimg_mp_constant(*list_median[p1]);
22038               }
22039               _cimg_mp_scalar1(mp_list_median,arg1);
22040             }
22041 
22042             if (*ss1=='n') { // in#ind
22043               if (!listin) _cimg_mp_return(0);
22044               if (_cimg_mp_is_constant(arg1)) {
22045                 if (!list_norm) list_norm.assign(listin._width);
22046                 if (!list_norm[p1]) CImg<doubleT>::vector(listin[p1].magnitude()).move_to(list_norm[p1]);
22047                 _cimg_mp_constant(*list_norm[p1]);
22048               }
22049               _cimg_mp_scalar1(mp_list_norm,arg1);
22050             }
22051 
22052             switch (*ss1) {
22053             case 'm' : arg2 = 0; break; // im#ind
22054             case 'M' : arg2 = 1; break; // iM#ind
22055             case 'a' : arg2 = 2; break; // ia#ind
22056             case 'v' : arg2 = 3; break; // iv#ind
22057             case 's' : arg2 = 12; break; // is#ind
22058             case 'p' : arg2 = 13; break; // ip#ind
22059             }
22060           } else if (*ss1=='m') switch (*ss) {
22061             case 'x' : arg2 = 4; break; // xm#ind
22062             case 'y' : arg2 = 5; break; // ym#ind
22063             case 'z' : arg2 = 6; break; // zm#ind
22064             case 'c' : arg2 = 7; break; // cm#ind
22065             } else if (*ss1=='M') switch (*ss) {
22066             case 'x' : arg2 = 8; break; // xM#ind
22067             case 'y' : arg2 = 9; break; // yM#ind
22068             case 'z' : arg2 = 10; break; // zM#ind
22069             case 'c' : arg2 = 11; break; // cM#ind
22070             }
22071           if (arg2!=~0U) {
22072             if (!listin) _cimg_mp_return(0);
22073             if (_cimg_mp_is_constant(arg1)) {
22074               if (!list_stats) list_stats.assign(listin._width);
22075               if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false);
22076               _cimg_mp_constant(list_stats(p1,arg2));
22077             }
22078             _cimg_mp_scalar2(mp_list_stats,arg1,arg2);
22079           }
22080         }
22081 
22082         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
22083           arg1 = compile(ss4,se,depth1,0,is_critical);
22084           if (!listin) _cimg_mp_return(0);
22085           p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22086           if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth);
22087           _cimg_mp_scalar1(mp_list_whd,arg1);
22088         }
22089         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
22090           arg1 = compile(ss5,se,depth1,0,is_critical);
22091           if (!listin) _cimg_mp_return(0);
22092           p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
22093           if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth*listin[p1]._spectrum);
22094           _cimg_mp_scalar1(mp_list_whds,arg1);
22095         }
22096 
22097         if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation
22098         if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary
22099 
22100         // No known item found, assuming this is an already initialized variable.
22101         variable_name.assign(ss,(unsigned int)(se + 1 - ss)).back() = 0;
22102         is_sth = true; // is_valid_variable_name?
22103         if (*variable_name>='0' && *variable_name<='9') is_sth = false;
22104         else for (ns = variable_name; *ns; ++ns)
22105                if (!is_varchar(*ns)) { is_sth = false; break; }
22106         if (is_sth) {
22107           get_variable_pos(variable_name,arg1,arg2);
22108           arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
22109           if (arg1!=~0U) _cimg_mp_return(arg1);
22110         }
22111 
22112         // Reached an unknown item -> error.
22113         c1 = *se1;
22114         _cimg_mp_strerr;
22115         cimg::strellipsize(variable_name,64);
22116         if (is_sth)
22117           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22118                                       "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.",
22119                                       pixel_type(),_cimg_mp_calling_function,
22120                                       variable_name._data,
22121                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22122         s1 = std::strchr(ss,'(');
22123         s_op = s1 && c1==')'?"function call":"item";
22124         throw CImgArgumentException("[" cimg_appname "_math_parser] "
22125                                     "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.",
22126                                     pixel_type(),_cimg_mp_calling_function,
22127                                     s_op,variable_name._data,
22128                                     s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22129       }
22130 
22131       // Evaluation procedure.
22132       double operator()(const double x, const double y, const double z, const double c) {
22133         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
22134         for (p_code = code; p_code<p_code_end; ++p_code) {
22135           opcode._data = p_code->_data;
22136           const ulongT target = opcode[1];
22137           mem[target] = _cimg_mp_defunc(*this);
22138         }
22139         return *result;
22140       }
22141 
22142       // Evaluation procedure (return output values in vector 'output').
22143       template<typename t>
22144       void operator()(const double x, const double y, const double z, const double c, t *const output) {
22145         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
22146         for (p_code = code; p_code<p_code_end; ++p_code) {
22147           opcode._data = p_code->_data;
22148           const ulongT target = opcode[1];
22149           mem[target] = _cimg_mp_defunc(*this);
22150         }
22151         if (result_dim) {
22152           const double *ptrs = result + 1;
22153           t *ptrd = output;
22154           for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++);
22155         } else *output = (t)*result;
22156       }
22157 
22158       // Evaluation procedure for begin_t() bloc.
22159       void begin_t() {
22160         if (!code_begin_t) return;
22161         if (imgin) {
22162           mem[_cimg_mp_slot_x] = imgin._width - 1.;
22163           mem[_cimg_mp_slot_y] = imgin._height - 1.;
22164           mem[_cimg_mp_slot_z] = imgin._depth - 1.;
22165           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
22166         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22167         p_code_end = code_begin_t.end();
22168         for (p_code = code_begin_t; p_code<p_code_end; ++p_code) {
22169           opcode._data = p_code->_data;
22170           const ulongT target = opcode[1];
22171           mem[target] = _cimg_mp_defunc(*this);
22172         }
22173         p_code_end = code.end();
22174       }
22175 
22176       // Evaluation procedure for end_t() bloc.
22177       void end_t() {
22178         if (!code_end_t) return;
22179         if (imgin) {
22180           mem[_cimg_mp_slot_x] = imgin._width - 1.;
22181           mem[_cimg_mp_slot_y] = imgin._height - 1.;
22182           mem[_cimg_mp_slot_z] = imgin._depth - 1.;
22183           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
22184         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22185         p_code_end = code_end_t.end();
22186         for (p_code = code_end_t; p_code<p_code_end; ++p_code) {
22187           opcode._data = p_code->_data;
22188           const ulongT target = opcode[1];
22189           mem[target] = _cimg_mp_defunc(*this);
22190         }
22191       }
22192 
22193       // Evaluation procedure the end() bloc.
22194       void end() {
22195         if (!code_end) return;
22196         if (imgin) {
22197           mem[_cimg_mp_slot_x] = imgin._width - 1.;
22198           mem[_cimg_mp_slot_y] = imgin._height - 1.;
22199           mem[_cimg_mp_slot_z] = imgin._depth - 1.;
22200           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
22201         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
22202         p_code_end = code_end.end();
22203         for (p_code = code_end; p_code<p_code_end; ++p_code) {
22204           opcode._data = p_code->_data;
22205           const ulongT target = opcode[1];
22206           mem[target] = _cimg_mp_defunc(*this);
22207         }
22208       }
22209 
22210       // Merge inter-thread variables.
22211       // (argument 'mp' is the master instance).
22212       void merge(_cimg_math_parser& mp) {
22213         if (&mp==this) return;
22214         cimg_rofY(mp.memmerge,k) {
22215           const unsigned int
22216             pos = (unsigned int)mp.memmerge(0,k),
22217             siz = (unsigned int)mp.memmerge(1,k),
22218             iop = (unsigned int)mp.memmerge(2,k);
22219           if (!siz) switch (iop) { // Scalar value
22220             case 0 : mp.mem[pos] = mem[pos]; break;                       // Assignment
22221             case 1 : mp.mem[pos]+=mem[pos]; break;                        // Operator+
22222             case 2 : mp.mem[pos]-=mem[pos]; break;                        // Operator-
22223             case 3 : mp.mem[pos]*=mem[pos]; break;                        // Operator*
22224             case 4 : mp.mem[pos]/=mem[pos]; break;                        // Operator/
22225             case 5 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min'
22226             case 6 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max'
22227             } else switch (iop) { // Vector value
22228             case 0 :
22229               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true) = CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22230               break;
22231             case 1 :
22232               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22233               break;
22234             case 2 :
22235               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22236               break;
22237             case 3 :
22238               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22239               break;
22240             case 4 :
22241               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
22242               break;
22243             case 5 :
22244               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
22245               break;
22246             case 6 :
22247               CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
22248               break;
22249             }
22250         }
22251       }
22252 
22253       // Return specified argument number as a string.
22254       static const char *s_argth(const unsigned int n_arg) {
22255         const char
22256           *_s_arg[] = { "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth","Ninth",
22257                         "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
22258                         "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "One of the" };
22259         return _s_arg[n_arg<sizeof(_s_arg)/sizeof(char*)?n_arg:sizeof(_s_arg)/sizeof(char*)-1];
22260       }
22261 
22262       // Return a string that defines the calling function + the user-defined function scope.
22263       CImg<charT> s_calling_function() const {
22264         CImg<charT> res;
22265         const unsigned int
22266           l1 = calling_function?(unsigned int)std::strlen(calling_function):0U,
22267           l2 = user_macro?(unsigned int)std::strlen(user_macro):0U;
22268         if (l2) {
22269           res.assign(l1 + l2 + 48);
22270           cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro);
22271         } else {
22272           res.assign(l1 + l2 + 4);
22273           cimg_snprintf(res,res._width,"%s()",calling_function);
22274         }
22275         return res;
22276       }
22277 
22278       // Return type of a memory element as a string.
22279       CImg<charT> s_type(const unsigned int arg) const {
22280         CImg<charT> res;
22281         if (_cimg_mp_is_vector(arg)) { // Vector
22282           CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res);
22283           cimg_sprintf(res._data + 6,"%u",_cimg_mp_size(arg));
22284         } else CImg<charT>::string("scalar").move_to(res);
22285         return res;
22286       }
22287 
22288       // Count parentheses/brackets level of each character of the expression.
22289       CImg<uintT> get_level(CImg<charT>& _expr) const {
22290         bool is_escaped = false, next_is_escaped = false;
22291         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
22292         CImg<uintT> res(_expr._width - 1);
22293         unsigned int *pd = res._data;
22294         int _level = 0;
22295         for (const char *ps = _expr._data; *ps && _level>=0; ++ps) {
22296           if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
22297           if (!is_escaped && *ps=='\'') { // Non-escaped character
22298             if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
22299             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
22300             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
22301           }
22302           *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1):
22303                                    *ps=='(' || *ps=='['?_level++:
22304                                    *ps==')' || *ps==']'?--_level:
22305                                    _level);
22306           mode = next_mode;
22307           is_escaped = next_is_escaped;
22308           next_is_escaped = false;
22309         }
22310         if (mode) {
22311           cimg::strellipsize(_expr,64);
22312           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22313                                       "CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
22314                                       pixel_type(),_cimg_mp_calling_function,
22315                                       _expr._data);
22316         }
22317         if (_level) {
22318           cimg::strellipsize(_expr,64);
22319           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22320                                       "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
22321                                       pixel_type(),_cimg_mp_calling_function,
22322                                       _expr._data);
22323         }
22324         return res;
22325       }
22326 
22327       // Find and return index of current image 'imgin' within image list 'listin'.
22328       unsigned int get_mem_img_index() {
22329         if (mem_img_index==~0U) {
22330           if (&imgout>listout.data() && &imgout<listout.end())
22331             mem_img_index = constant((double)(&imgout - listout.data()));
22332           else {
22333             unsigned int pos = ~0U;
22334             cimglist_for(listout,l)
22335               if (imgout._data==listout[l]._data && imgout.is_sameXYZC(listout[l])) { pos = l; break; }
22336             if (pos!=~0U) mem_img_index = constant((double)pos);
22337           }
22338         }
22339         return mem_img_index;
22340       }
22341 
22342       // Return indices for accessing math parser variables.
22343       void get_variable_pos(const char *variable_name, unsigned int &pos, unsigned int &rpos) {
22344         char c1, c2, c3, c4;
22345         pos = rpos = ~0U;
22346         if (!variable_name || !*variable_name) return;
22347 
22348         unsigned int rp = variable_name[1]?~0U:*variable_name; // One-char variable
22349         if (variable_name[1] && !variable_name[2]) { // Two-chars variable
22350           c1 = variable_name[0];
22351           c2 = variable_name[1];
22352           if (c1=='w' && c2=='h') rp = 0; // wh
22353           else if (c1=='p' && c2=='i') rp = 3; // pi
22354           else if (c1=='i') {
22355             if (c2>='0' && c2<='9') rp = 20 + c2 - '0'; // i0...i9
22356             else if (c2=='m') rp = 4; // im
22357             else if (c2=='M') rp = 5; // iM
22358             else if (c2=='a') rp = 6; // ia
22359             else if (c2=='v') rp = 7; // iv
22360             else if (c2=='s') rp = 8; // is
22361             else if (c2=='p') rp = 9; // ip
22362             else if (c2=='c') rp = 10; // ic
22363             else if (c2=='n') rp = 11; // in
22364           } else if (c2=='m') {
22365             if (c1=='x') rp = 12; // xm
22366             else if (c1=='y') rp = 13; // ym
22367             else if (c1=='z') rp = 14; // zm
22368             else if (c1=='c') rp = 15; // cm
22369           } else if (c2=='M') {
22370             if (c1=='x') rp = 16; // xM
22371             else if (c1=='y') rp = 17; // yM
22372             else if (c1=='z') rp = 18; // zM
22373             else if (c1=='c') rp = 19; // cM
22374           }
22375         } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable
22376           c1 = variable_name[0];
22377           c2 = variable_name[1];
22378           c3 = variable_name[2];
22379           if (c1=='w' && c2=='h' && c3=='d') rp = 1; // whd
22380         } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
22381                    !variable_name[4]) { // Four-chars variable
22382           c1 = variable_name[0];
22383           c2 = variable_name[1];
22384           c3 = variable_name[2];
22385           c4 = variable_name[3];
22386           if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds
22387         } else if (!std::strcmp(variable_name,"interpolation")) rp = 30; // interpolation
22388         else if (!std::strcmp(variable_name,"boundary")) rp = 31; // boundary
22389 
22390         if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels
22391 
22392         // Multi-char variable name : check for existing variable with same name
22393         cimglist_for(variable_def,i)
22394           if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; }
22395       }
22396 
22397       // Tell for each character of an expression if it is inside a string or not.
22398       CImg<boolT> is_inside_string(CImg<charT>& _expr) const {
22399         bool is_escaped = false, next_is_escaped = false;
22400         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
22401         CImg<boolT> res = CImg<charT>::string(_expr);
22402         bool *pd = res._data;
22403         for (const char *ps = _expr._data; *ps; ++ps) {
22404           if (!next_is_escaped && *ps=='\\') next_is_escaped = true;
22405           if (!is_escaped && *ps=='\'') { // Non-escaped character
22406             if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
22407             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
22408             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
22409           }
22410           *(pd++) = mode>=1 || is_escaped;
22411           mode = next_mode;
22412           is_escaped = next_is_escaped;
22413           next_is_escaped = false;
22414         }
22415         return res;
22416       }
22417 
22418       // Return true if specified argument can be a part of an allowed  variable name.
22419       bool is_varchar(const char c) const {
22420         return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
22421       }
22422 
22423       // Return true if all values of a vector are computation values.
22424       bool is_comp_vector(const unsigned int arg) const {
22425         unsigned int siz = _cimg_mp_size(arg);
22426         if (siz>8) return false;
22427         const int *ptr = memtype.data(arg + 1);
22428         bool is_tmp = true;
22429         while (siz-->0) if (*(ptr++)) { is_tmp = false; break; }
22430         return is_tmp;
22431       }
22432 
22433       // Check if a memory slot is a positive integer constant scalar value.
22434       // 'mode' can be:
22435       // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant }
22436       void check_constant(const unsigned int arg, const unsigned int n_arg,
22437                           const unsigned int mode,
22438                           char *const ss, char *const se, const char saved_char) {
22439         _cimg_mp_check_type(arg,n_arg,1,0);
22440         if (!(_cimg_mp_is_constant(arg) &&
22441               (!mode || (double)(int)mem[arg]==mem[arg]) &&
22442               (mode<2 || mem[arg]>=(mode==3)))) {
22443           const char *const s_arg = s_argth(n_arg);
22444           char *s0; _cimg_mp_strerr;
22445           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22446                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, "
22447                                       "in expression '%s%s%s'.",
22448                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22449                                       s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,
22450                                       !mode?"":mode==1?"n integer":
22451                                       mode==2?" positive integer":" strictly positive integer",
22452                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22453         }
22454       }
22455 
22456       // Check if an image index is a constant value.
22457       void check_constant_index(const unsigned int arg,
22458                                 char *const ss, char *const se, const char saved_char) {
22459         if (arg!=~0U && !_cimg_mp_is_constant(arg)) {
22460           char *s0; _cimg_mp_strerr;
22461           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22462                                       "CImg<%s>::%s: %s%s Specified image index is not a constant, "
22463                                       "in expression '%s%s%s'.",
22464                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22465                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22466         }
22467       }
22468 
22469       // Check a matrix is square.
22470       void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
22471                                char *const ss, char *const se, const char saved_char) {
22472         _cimg_mp_check_type(arg,n_arg,2,0);
22473         const unsigned int
22474           siz = _cimg_mp_size(arg),
22475           n = (unsigned int)cimg::round(std::sqrt((float)siz));
22476         if (n*n!=siz) {
22477           const char *s_arg;
22478           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
22479           else s_arg = !n_arg?"":n_arg==1?"First":n_arg==2?"Second":n_arg==3?"Third":"One";
22480           char *s0; _cimg_mp_strerr;
22481           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22482                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') "
22483                                       "cannot be considered as a square matrix, in expression '%s%s%s'.",
22484                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22485                                       s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
22486                                       s_type(arg)._data,
22487                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22488         }
22489       }
22490 
22491       // Check type compatibility for one argument.
22492       // Bits of 'mode' tells what types are allowed:
22493       // { 1 = scalar | 2 = vectorN }.
22494       // If 'N' is not zero, it also restricts the vectors to be of size N only.
22495       void check_type(const unsigned int arg, const unsigned int n_arg,
22496                       const unsigned int mode, const unsigned int N,
22497                       char *const ss, char *const se, const char saved_char) {
22498         const bool
22499           is_scalar = _cimg_mp_is_scalar(arg),
22500           is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N);
22501         bool cond = false;
22502         if (mode&1) cond|=is_scalar;
22503         if (mode&2) cond|=is_vector;
22504         if (!cond) {
22505           const char *s_arg;
22506           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
22507           else s_arg = s_argth(n_arg);
22508           CImg<charT> sb_type(32);
22509           if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'");
22510           else if (mode==2) {
22511             if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N);
22512             else cimg_snprintf(sb_type,sb_type._width,"'vector'");
22513           } else {
22514             if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N);
22515             else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'");
22516           }
22517           char *s0; _cimg_mp_strerr;
22518           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22519                                       "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), "
22520                                       "in expression '%s%s%s'.",
22521                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22522                                       s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
22523                                       s_type(arg)._data,sb_type._data,
22524                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22525         }
22526       }
22527 
22528       // Check that listin or listout are not empty.
22529       void check_list(const bool is_out,
22530                       char *const ss, char *const se, const char saved_char) {
22531         if ((!is_out && !listin) || (is_out && !listout)) {
22532           char *s0; _cimg_mp_strerr;
22533           throw CImgArgumentException("[" cimg_appname "_math_parser] "
22534                                       "CImg<%s>::%s: %s%s Invalid call with an empty image list, "
22535                                       "in expression '%s%s%s'.",
22536                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
22537                                       s0>expr._data?"...":"",s0,se<&expr.back()?"...":"");
22538         }
22539       }
22540 
22541       // Insert constant value in memory.
22542       unsigned int constant(const double val) {
22543 
22544         // Search for built-in constant.
22545         if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan;
22546         if (val==(double)(int)val) {
22547           if (val>=0 && val<=10) return (unsigned int)val;
22548           if (val<0 && val>=-5) return (unsigned int)(10 - val);
22549         }
22550         if (val==0.5) return 16;
22551 
22552         // Search for constant already requested before (in const cache).
22553         unsigned int ind = ~0U;
22554         if (constcache_size<1024) {
22555           if (!constcache_size) {
22556             constcache_vals.assign(16,1,1,1,0);
22557             constcache_inds.assign(16,1,1,1,0);
22558             *constcache_vals = val;
22559             constcache_size = 1;
22560             ind = 0;
22561           } else { // Dichotomic search
22562             const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1];
22563             if (val_beg>=val) ind = 0;
22564             else if (val_end==val) ind = constcache_size - 1;
22565             else if (val_end<val) ind = constcache_size;
22566             else {
22567               unsigned int i0 = 1, i1 = constcache_size - 2;
22568               while (i0<=i1) {
22569                 const unsigned int mid = (i0 + i1)/2;
22570                 if (constcache_vals[mid]==val) { i0 = mid; break; }
22571                 else if (constcache_vals[mid]<val) i0 = mid + 1;
22572                 else i1 = mid - 1;
22573               }
22574               ind = i0;
22575             }
22576 
22577             if (ind>=constcache_size || constcache_vals[ind]!=val) {
22578               ++constcache_size;
22579               if (constcache_size>constcache_vals._width) {
22580                 constcache_vals.resize(-200,1,1,1,0);
22581                 constcache_inds.resize(-200,1,1,1,0);
22582               }
22583               const int l = constcache_size - (int)ind - 1;
22584               if (l>0) {
22585                 std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double));
22586                 std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int));
22587               }
22588               constcache_vals[ind] = val;
22589               constcache_inds[ind] = 0;
22590             }
22591           }
22592           if (constcache_inds[ind]) return constcache_inds[ind];
22593         }
22594 
22595         // Insert new constant in memory if necessary.
22596         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); }
22597         const unsigned int pos = mempos++;
22598         mem[pos] = val;
22599         memtype[pos] = 1; // Set constant property
22600         if (ind!=~0U) constcache_inds[ind] = pos;
22601         return pos;
22602       }
22603 
22604       // Insert new scalar in memory.
22605       unsigned int scalar() {
22606         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); }
22607         return mempos++;
22608       }
22609 
22610       // Insert new vector of specified size in memory.
22611       unsigned int vector(const unsigned int siz) {
22612         if (mempos + siz>=mem._width) {
22613           mem.resize(2*mem._width + siz,1,1,1,0);
22614           memtype.resize(mem._width,1,1,1,0);
22615         }
22616         const unsigned int pos = mempos++;
22617         mem[pos] = cimg::type<double>::nan();
22618         memtype[pos] = siz + 1;
22619         mempos+=siz;
22620         return pos;
22621       }
22622 
22623       // Insert new initialized vector.
22624       unsigned int vector(const unsigned int siz, const double value) {
22625         const unsigned int pos = vector(siz);
22626         double *ptr = &mem[pos] + 1;
22627         for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value;
22628         return pos;
22629       }
22630 
22631       // Insert new copy of specified vector in memory.
22632       unsigned int vector_copy(const unsigned int arg) {
22633         const unsigned int
22634           siz = _cimg_mp_size(arg),
22635           pos = vector(siz);
22636         CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code);
22637         return pos;
22638       }
22639 
22640       // Set variable status to all values of a vector.
22641       void set_variable_vector(const unsigned int arg) {
22642         unsigned int siz = _cimg_mp_size(arg);
22643         int *ptr = memtype.data(arg + 1);
22644         while (siz-->0) *(ptr++) = -1;
22645       }
22646 
22647       unsigned int scalar0(const mp_func op) {
22648         const unsigned int pos = scalar();
22649         CImg<ulongT>::vector((ulongT)op,pos).move_to(code);
22650         return pos;
22651       }
22652 
22653       unsigned int scalar1(const mp_func op, const unsigned int arg1) {
22654         const unsigned int pos =
22655           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:scalar();
22656         CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
22657         return pos;
22658       }
22659 
22660       unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
22661         const unsigned int pos =
22662           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22663           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:scalar();
22664         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code);
22665         return pos;
22666       }
22667 
22668       unsigned int scalar3(const mp_func op,
22669                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
22670         const unsigned int pos =
22671           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22672           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22673           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:scalar();
22674         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code);
22675         return pos;
22676       }
22677 
22678       unsigned int scalar4(const mp_func op,
22679                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
22680                            const unsigned int arg4) {
22681         const unsigned int pos =
22682           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22683           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22684           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22685           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:scalar();
22686         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code);
22687         return pos;
22688       }
22689 
22690       unsigned int scalar5(const mp_func op,
22691                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
22692                            const unsigned int arg4, const unsigned int arg5) {
22693         const unsigned int pos =
22694           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22695           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22696           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22697           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
22698           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:scalar();
22699         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
22700         return pos;
22701       }
22702 
22703       unsigned int scalar6(const mp_func op,
22704                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
22705                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
22706         const unsigned int pos =
22707           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22708           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22709           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22710           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
22711           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
22712           arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:scalar();
22713         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
22714         return pos;
22715       }
22716 
22717       unsigned int scalar7(const mp_func op,
22718                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
22719                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6,
22720                            const unsigned int arg7) {
22721         const unsigned int pos =
22722           arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
22723           arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
22724           arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
22725           arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
22726           arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
22727           arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
22728           arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:scalar();
22729         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code);
22730         return pos;
22731       }
22732 
22733       void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) {
22734         const unsigned int siz = _cimg_mp_size(pos);
22735         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code);
22736         else {
22737           code.insert(siz);
22738           for (unsigned int k = 1; k<=siz; ++k)
22739             CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]);
22740         }
22741       }
22742 
22743       void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) {
22744         const unsigned int siz = _cimg_mp_size(pos);
22745         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code);
22746         else {
22747           code.insert(siz);
22748           for (unsigned int k = 1; k<=siz; ++k)
22749             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
22750         }
22751       }
22752 
22753       unsigned int vector1_v(const mp_func op, const unsigned int arg1) {
22754         const unsigned int
22755           siz = _cimg_mp_size(arg1),
22756           pos = is_comp_vector(arg1)?arg1:vector(siz);
22757         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code);
22758         else {
22759           code.insert(siz);
22760           for (unsigned int k = 1; k<=siz; ++k)
22761             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
22762         }
22763         return pos;
22764       }
22765 
22766       unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
22767         const unsigned int
22768           siz = _cimg_mp_size(arg1),
22769           pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:vector(siz);
22770         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
22771         else {
22772           code.insert(siz);
22773           for (unsigned int k = 1; k<=siz; ++k)
22774             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]);
22775         }
22776         return pos;
22777       }
22778 
22779       unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
22780         const unsigned int
22781           siz = _cimg_mp_size(arg1),
22782           pos = is_comp_vector(arg1)?arg1:vector(siz);
22783         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
22784         else {
22785           code.insert(siz);
22786           for (unsigned int k = 1; k<=siz; ++k)
22787             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
22788         }
22789         return pos;
22790       }
22791 
22792       unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
22793         const unsigned int
22794           siz = _cimg_mp_size(arg2),
22795           pos = is_comp_vector(arg2)?arg2:vector(siz);
22796         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
22797         else {
22798           code.insert(siz);
22799           for (unsigned int k = 1; k<=siz; ++k)
22800             CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
22801         }
22802         return pos;
22803       }
22804 
22805       unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
22806                                const unsigned int arg3) {
22807         const unsigned int
22808           siz = _cimg_mp_size(arg1),
22809           pos = is_comp_vector(arg1)?arg1:vector(siz);
22810         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code);
22811         else {
22812           code.insert(siz);
22813           for (unsigned int k = 1; k<=siz; ++k)
22814             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]);
22815         }
22816         return pos;
22817       }
22818 
22819       // Evaluation functions, known by the parser.
22820       // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT),
22821       // so we can store pointers to them directly in the opcode vectors.
22822 #ifdef _mp_arg
22823 #undef _mp_arg
22824 #endif
22825 #define _mp_arg(x) mp.mem[mp.opcode[x]]
22826 
22827       static double mp_abs(_cimg_math_parser& mp) {
22828         return cimg::abs(_mp_arg(2));
22829       }
22830 
22831       static double mp_add(_cimg_math_parser& mp) {
22832         return _mp_arg(2) + _mp_arg(3);
22833       }
22834 
22835       static double mp_acos(_cimg_math_parser& mp) {
22836         return std::acos(_mp_arg(2));
22837       }
22838 
22839       static double mp_acosh(_cimg_math_parser& mp) {
22840         return cimg::acosh(_mp_arg(2));
22841       }
22842 
22843       static double mp_asinh(_cimg_math_parser& mp) {
22844         return cimg::asinh(_mp_arg(2));
22845       }
22846 
22847       static double mp_atanh(_cimg_math_parser& mp) {
22848         return cimg::atanh(_mp_arg(2));
22849       }
22850 
22851       static double mp_arg(_cimg_math_parser& mp) {
22852         const int _ind = (int)_mp_arg(4);
22853         const unsigned int
22854           nb_args = (unsigned int)mp.opcode[2] - 4,
22855           ind = _ind<0?_ind + nb_args:(unsigned int)_ind,
22856           siz = (unsigned int)mp.opcode[3];
22857         if (siz>0) {
22858           if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
22859           else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
22860           return cimg::type<double>::nan();
22861         }
22862         if (ind>=nb_args) return 0;
22863         return _mp_arg(ind + 4);
22864       }
22865 
22866       static double mp_arg0(_cimg_math_parser& mp) {
22867         const int _ind = (int)_mp_arg(4);
22868         const unsigned int
22869           nb_args = (unsigned int)mp.opcode[2] - 4,
22870           ind = _ind<0?_ind + nb_args:_ind + 1U,
22871           siz = (unsigned int)mp.opcode[3];
22872         if (siz>0) {
22873           if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
22874           else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
22875           return cimg::type<double>::nan();
22876         }
22877         if (ind>=nb_args) return 0;
22878         return _mp_arg(ind + 4);
22879       }
22880 
22881       static double mp_argkth(_cimg_math_parser& mp) {
22882         const unsigned int i_end = (unsigned int)mp.opcode[2];
22883         const double val = mp_kth(mp);
22884         for (unsigned int i = 4; i<i_end; ++i) if (val==_mp_arg(i)) return i - 3.;
22885         return 1.;
22886       }
22887 
22888       static double mp_argmin(_cimg_math_parser& mp) {
22889         const unsigned int i_end = (unsigned int)mp.opcode[2];
22890         double val = _mp_arg(3);
22891         unsigned int argval = 0;
22892         for (unsigned int i = 4; i<i_end; ++i) {
22893           const double _val = _mp_arg(i);
22894           if (_val<val) { val = _val; argval = i - 3; }
22895         }
22896         return (double)argval;
22897       }
22898 
22899       static double mp_argminabs(_cimg_math_parser& mp) {
22900         const unsigned int i_end = (unsigned int)mp.opcode[2];
22901         double val = _mp_arg(3), absval = cimg::abs(val);
22902         unsigned int argval = 0;
22903         for (unsigned int i = 4; i<i_end; ++i) {
22904           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
22905           if (_absval<absval) { val = _val; absval = _absval; argval = i - 3; }
22906         }
22907         return (double)argval;
22908       }
22909 
22910       static double mp_argmax(_cimg_math_parser& mp) {
22911         const unsigned int i_end = (unsigned int)mp.opcode[2];
22912         double val = _mp_arg(3);
22913         unsigned int argval = 0;
22914         for (unsigned int i = 4; i<i_end; ++i) {
22915           const double _val = _mp_arg(i);
22916           if (_val>val) { val = _val; argval = i - 3; }
22917         }
22918         return (double)argval;
22919       }
22920 
22921       static double mp_argmaxabs(_cimg_math_parser& mp) {
22922         const unsigned int i_end = (unsigned int)mp.opcode[2];
22923         double val = _mp_arg(3), absval = cimg::abs(val);
22924         unsigned int argval = 0;
22925         for (unsigned int i = 4; i<i_end; ++i) {
22926           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
22927           if (_absval>absval) { val = _val; absval = _absval; argval = i - 3; }
22928         }
22929         return (double)argval;
22930       }
22931 
22932       static double mp_asin(_cimg_math_parser& mp) {
22933         return std::asin(_mp_arg(2));
22934       }
22935 
22936       static double mp_atan(_cimg_math_parser& mp) {
22937         return std::atan(_mp_arg(2));
22938       }
22939 
22940       static double mp_atan2(_cimg_math_parser& mp) {
22941         return std::atan2(_mp_arg(2),_mp_arg(3));
22942       }
22943 
22944       static double mp_avg(_cimg_math_parser& mp) {
22945         const unsigned int i_end = (unsigned int)mp.opcode[2];
22946         double val = _mp_arg(3);
22947         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
22948         return val/(i_end - 3);
22949       }
22950 
22951       static double mp_bitwise_and(_cimg_math_parser& mp) {
22952         return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3));
22953       }
22954 
22955       static double mp_bitwise_left_shift(_cimg_math_parser& mp) {
22956         return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3));
22957       }
22958 
22959       static double mp_bitwise_not(_cimg_math_parser& mp) {
22960         // Limit result to 32bits such that it can be entirely represented as a 'double'.
22961         return (double)~(unsigned int)_mp_arg(2);
22962       }
22963 
22964       static double mp_bitwise_or(_cimg_math_parser& mp) {
22965         return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3));
22966       }
22967 
22968       static double mp_bitwise_right_shift(_cimg_math_parser& mp) {
22969         return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3));
22970       }
22971 
22972       static double mp_bitwise_xor(_cimg_math_parser& mp) {
22973         return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3));
22974       }
22975 
22976       static double mp_bool(_cimg_math_parser& mp) {
22977         return (double)(bool)_mp_arg(2);
22978       }
22979 
22980       static double mp_break(_cimg_math_parser& mp) {
22981         mp.break_type = 1;
22982         mp.p_code = mp.p_break - 1;
22983         return cimg::type<double>::nan();
22984       }
22985 
22986       static double mp_breakpoint(_cimg_math_parser& mp) {
22987         cimg_abort_init;
22988         cimg_abort_test;
22989         cimg::unused(mp);
22990         return cimg::type<double>::nan();
22991       }
22992 
22993 #ifdef cimg_mp_func_run
22994       static double mp_run(_cimg_math_parser& mp) {
22995         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
22996         CImgList<charT> _str;
22997         CImg<charT> it;
22998         for (unsigned int n = 0; n<nb_args; ++n) {
22999           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
23000           if (siz) { // Vector argument -> string
23001             const double *ptr = &_mp_arg(3 + 2*n) + 1;
23002             unsigned int l = 0;
23003             while (l<siz && ptr[l]) ++l;
23004             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
23005           } else { // Scalar argument -> number
23006             it.assign(24);
23007             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
23008             CImg<charT>::string(it,false,true).move_to(_str);
23009           }
23010         }
23011         CImg(1,1,1,1,0).move_to(_str);
23012         CImg<charT> str = _str>'x';
23013         cimg_mp_func_run(str._data);
23014         return cimg::type<double>::nan();
23015       }
23016 #endif
23017 
23018       static double mp_cbrt(_cimg_math_parser& mp) {
23019         return cimg::cbrt(_mp_arg(2));
23020       }
23021 
23022       static double mp_ceil(_cimg_math_parser& mp) {
23023         return std::ceil(_mp_arg(2));
23024       }
23025 
23026       static double mp_complex_abs(_cimg_math_parser& mp) {
23027         return cimg::_hypot(_mp_arg(2),_mp_arg(3));
23028       }
23029 
23030       static double mp_complex_conj(_cimg_math_parser& mp) {
23031         const double real = _mp_arg(2), imag = _mp_arg(3);
23032         double *ptrd = &_mp_arg(1) + 1;
23033         ptrd[0] = real;
23034         ptrd[1] = -imag;
23035         return cimg::type<double>::nan();
23036       }
23037 
23038       static double mp_complex_div_sv(_cimg_math_parser& mp) {
23039         const double
23040           *ptr2 = &_mp_arg(3) + 1,
23041           r1 = _mp_arg(2),
23042           r2 = *(ptr2++), i2 = *ptr2;
23043         double *ptrd = &_mp_arg(1) + 1;
23044         const double denom = r2*r2 + i2*i2;
23045         *(ptrd++) = r1*r2/denom;
23046         *ptrd =  -r1*i2/denom;
23047         return cimg::type<double>::nan();
23048       }
23049 
23050       static double mp_complex_div_vv(_cimg_math_parser& mp) {
23051         const double
23052           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
23053           r1 = *(ptr1++), i1 = *ptr1,
23054           r2 = *(ptr2++), i2 = *ptr2;
23055         double *ptrd = &_mp_arg(1) + 1;
23056         const double denom = r2*r2 + i2*i2;
23057         *(ptrd++) = (r1*r2 + i1*i2)/denom;
23058         *ptrd = (r2*i1 - r1*i2)/denom;
23059         return cimg::type<double>::nan();
23060       }
23061 
23062       static double mp_complex_exp(_cimg_math_parser& mp) {
23063         const double real = _mp_arg(2), imag = _mp_arg(3), exp_real = std::exp(real);
23064         double *ptrd = &_mp_arg(1) + 1;
23065         ptrd[0] = exp_real*std::cos(imag);
23066         ptrd[1] = exp_real*std::sin(imag);
23067         return cimg::type<double>::nan();
23068       }
23069 
23070       static double mp_complex_log(_cimg_math_parser& mp) {
23071         const double real = _mp_arg(2), imag = _mp_arg(3);
23072         double *ptrd = &_mp_arg(1) + 1;
23073         ptrd[0] = 0.5*std::log(real*real + imag*imag);
23074         ptrd[1] = std::atan2(imag,real);
23075         return cimg::type<double>::nan();
23076       }
23077 
23078       static double mp_complex_mul(_cimg_math_parser& mp) {
23079         const double
23080           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
23081           r1 = *(ptr1++), i1 = *ptr1,
23082           r2 = *(ptr2++), i2 = *ptr2;
23083         double *ptrd = &_mp_arg(1) + 1;
23084         *(ptrd++) = r1*r2 - i1*i2;
23085         *(ptrd++) = r1*i2 + r2*i1;
23086         return cimg::type<double>::nan();
23087       }
23088 
23089       static void _mp_complex_pow(const double r1, const double i1,
23090                                   const double r2, const double i2,
23091                                   double *ptrd) {
23092         double ro, io;
23093         if (cimg::abs(i2)<1e-15) { // Exponent is real
23094           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) {
23095             if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; }
23096             else ro = io = 0;
23097           } else {
23098             const double
23099               mod1_2 = r1*r1 + i1*i1,
23100               phi1 = std::atan2(i1,r1),
23101               modo = std::pow(mod1_2,0.5*r2),
23102               phio = r2*phi1;
23103             ro = modo*std::cos(phio);
23104             io = modo*std::sin(phio);
23105           }
23106         } else { // Exponent is complex
23107           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0;
23108           const double
23109             mod1_2 = r1*r1 + i1*i1,
23110             phi1 = std::atan2(i1,r1),
23111             modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1),
23112             phio = r2*phi1 + 0.5*i2*std::log(mod1_2);
23113           ro = modo*std::cos(phio);
23114           io = modo*std::sin(phio);
23115         }
23116         *(ptrd++) = ro;
23117         *ptrd = io;
23118       }
23119 
23120       static double mp_complex_pow_ss(_cimg_math_parser& mp) {
23121         const double val1 = _mp_arg(2), val2 = _mp_arg(3);
23122         double *ptrd = &_mp_arg(1) + 1;
23123         _mp_complex_pow(val1,0,val2,0,ptrd);
23124         return cimg::type<double>::nan();
23125       }
23126 
23127       static double mp_complex_pow_sv(_cimg_math_parser& mp) {
23128         const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1;
23129         double *ptrd = &_mp_arg(1) + 1;
23130         _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd);
23131         return cimg::type<double>::nan();
23132       }
23133 
23134       static double mp_complex_pow_vs(_cimg_math_parser& mp) {
23135         const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3);
23136         double *ptrd = &_mp_arg(1) + 1;
23137         _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd);
23138         return cimg::type<double>::nan();
23139       }
23140 
23141       static double mp_complex_pow_vv(_cimg_math_parser& mp) {
23142         const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1;
23143         double *ptrd = &_mp_arg(1) + 1;
23144         _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd);
23145         return cimg::type<double>::nan();
23146       }
23147 
23148       static double mp_complex_cos(_cimg_math_parser& mp) {
23149         const double real = _mp_arg(2), imag = _mp_arg(3);
23150         double *ptrd = &_mp_arg(1) + 1;
23151         ptrd[0] = std::cos(real)*std::cosh(imag);
23152         ptrd[1] = -std::sin(real)*std::sinh(imag);
23153         return cimg::type<double>::nan();
23154       }
23155 
23156       static double mp_complex_sin(_cimg_math_parser& mp) {
23157         const double real = _mp_arg(2), imag = _mp_arg(3);
23158         double *ptrd = &_mp_arg(1) + 1;
23159         ptrd[0] = std::sin(real)*std::cosh(imag);
23160         ptrd[1] = std::cos(real)*std::sinh(imag);
23161         return cimg::type<double>::nan();
23162       }
23163 
23164       static double mp_complex_tan(_cimg_math_parser& mp) {
23165         const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag);
23166         double *ptrd = &_mp_arg(1) + 1;
23167         ptrd[0] = std::sin(2*real)/denom;
23168         ptrd[1] = std::sinh(2*imag)/denom;
23169         return cimg::type<double>::nan();
23170       }
23171 
23172       static double mp_complex_cosh(_cimg_math_parser& mp) {
23173         const double real = _mp_arg(2), imag = _mp_arg(3);
23174         double *ptrd = &_mp_arg(1) + 1;
23175         ptrd[0] = std::cosh(real)*std::cos(imag);
23176         ptrd[1] = std::sinh(real)*std::sin(imag);
23177         return cimg::type<double>::nan();
23178       }
23179 
23180       static double mp_complex_sinh(_cimg_math_parser& mp) {
23181         const double real = _mp_arg(2), imag = _mp_arg(3);
23182         double *ptrd = &_mp_arg(1) + 1;
23183         ptrd[0] = std::sinh(real)*std::cos(imag);
23184         ptrd[1] = std::cosh(real)*std::sin(imag);
23185         return cimg::type<double>::nan();
23186       }
23187 
23188       static double mp_complex_tanh(_cimg_math_parser& mp) {
23189         const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cosh(2*real) + std::cos(2*imag);
23190         double *ptrd = &_mp_arg(1) + 1;
23191         ptrd[0] = std::sinh(2*real)/denom;
23192         ptrd[1] = std::sin(2*imag)/denom;
23193         return cimg::type<double>::nan();
23194       }
23195 
23196       static double mp_continue(_cimg_math_parser& mp) {
23197         mp.break_type = 2;
23198         mp.p_code = mp.p_break - 1;
23199         return cimg::type<double>::nan();
23200       }
23201 
23202       static double mp_convolve(_cimg_math_parser &mp) {
23203         return _mp_correlate(mp,true);
23204       }
23205 
23206       static double mp_correlate(_cimg_math_parser &mp) {
23207         return _mp_correlate(mp,false);
23208       }
23209 
23210       static double _mp_correlate(_cimg_math_parser &mp, bool is_convolve) {
23211         double *ptrd = &_mp_arg(1) + 1;
23212         const double *const ptrA = &_mp_arg(2) + 1, *const ptrM = &_mp_arg(7) + 1;
23213         const unsigned int
23214           wA = (unsigned int)mp.opcode[3],
23215           hA = (unsigned int)mp.opcode[4],
23216           dA = (unsigned int)mp.opcode[5],
23217           sA = (unsigned int)mp.opcode[6],
23218           wM = (unsigned int)mp.opcode[8],
23219           hM = (unsigned int)mp.opcode[9],
23220           dM = (unsigned int)mp.opcode[10],
23221           sM = (unsigned int)mp.opcode[11],
23222           boundary_conditions = (unsigned int)_mp_arg(12),
23223           channel_mode = (unsigned int)mp.opcode[14],
23224           xcenter = (unsigned int)_mp_arg(15),
23225           ycenter = (unsigned int)_mp_arg(16),
23226           zcenter = (unsigned int)_mp_arg(17),
23227           xstart = (unsigned int)mp.opcode[18],
23228           ystart = (unsigned int)mp.opcode[19],
23229           zstart = (unsigned int)mp.opcode[20],
23230           xend = (unsigned int)mp.opcode[21],
23231           yend = (unsigned int)mp.opcode[22],
23232           zend = (unsigned int)mp.opcode[23];
23233         const bool
23234           is_normalized = (bool)_mp_arg(13);
23235         const float
23236           xstride = (float)_mp_arg(24),
23237           ystride = (float)_mp_arg(25),
23238           zstride = (float)_mp_arg(26),
23239           xdilation = (float)_mp_arg(27),
23240           ydilation = (float)_mp_arg(28),
23241           zdilation = (float)_mp_arg(29);
23242         CImg<doubleT> res;
23243         if (is_convolve) res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
23244                            get_convolve(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
23245                                         boundary_conditions,is_normalized,channel_mode,
23246                                         xcenter,ycenter,zcenter,
23247                                         xstart,ystart,zstart,
23248                                         xend,yend,zend,
23249                                         xstride,ystride,zstride,
23250                                         xdilation,ydilation,zdilation);
23251         else res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
23252                get_correlate(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
23253                              boundary_conditions,is_normalized,channel_mode,
23254                              xcenter,ycenter,zcenter,
23255                              xstart,ystart,zstart,
23256                              xend,yend,zend,
23257                              xstride,ystride,zstride,
23258                              xdilation,ydilation,zdilation);
23259         CImg<doubleT>(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res;
23260         return cimg::type<double>::nan();
23261       }
23262 
23263       static double mp_cos(_cimg_math_parser& mp) {
23264         return std::cos(_mp_arg(2));
23265       }
23266 
23267       static double mp_cosh(_cimg_math_parser& mp) {
23268         return std::cosh(_mp_arg(2));
23269       }
23270 
23271       static double mp_critical(_cimg_math_parser& mp) {
23272         const ulongT g_target = mp.opcode[1];
23273         cimg_pragma_openmp(critical(mp_critical))
23274         {
23275           for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2];
23276                mp.p_code<p_end; ++mp.p_code) { // Evaluate body
23277             mp.opcode._data = mp.p_code->_data;
23278             const ulongT target = mp.opcode[1];
23279             mp.mem[target] = _cimg_mp_defunc(mp);
23280           }
23281         }
23282         --mp.p_code;
23283         return mp.mem[g_target];
23284       }
23285 
23286       static double mp_crop(_cimg_math_parser& mp) {
23287         double *ptrd = &_mp_arg(1) + 1;
23288         const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6);
23289         const unsigned int
23290           dx = (unsigned int)mp.opcode[7],
23291           dy = (unsigned int)mp.opcode[8],
23292           dz = (unsigned int)mp.opcode[9],
23293           dc = (unsigned int)mp.opcode[10];
23294         const unsigned int boundary_conditions = (unsigned int)_mp_arg(11);
23295         unsigned int ind = (unsigned int)mp.opcode[2];
23296         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23297         const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[ind];
23298         if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double));
23299         else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c,
23300                                                                  x + dx - 1,y + dy - 1,
23301                                                                  z + dz - 1,c + dc - 1,
23302                                                                  boundary_conditions);
23303         return cimg::type<double>::nan();
23304       }
23305 
23306       static double mp_cross(_cimg_math_parser& mp) {
23307         CImg<doubleT>
23308           vout(&_mp_arg(1) + 1,1,3,1,1,true),
23309           v1(&_mp_arg(2) + 1,1,3,1,1,true),
23310           v2(&_mp_arg(3) + 1,1,3,1,1,true);
23311         (vout = v1).cross(v2);
23312         return cimg::type<double>::nan();
23313       }
23314 
23315       static double mp_cut(_cimg_math_parser& mp) {
23316         double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4);
23317         return val<cmin?cmin:val>cmax?cmax:val;
23318       }
23319 
23320       static double mp_date(_cimg_math_parser& mp) {
23321         const unsigned int
23322           siz_out = (unsigned int)mp.opcode[2],
23323           siz_arg1 = (unsigned int)mp.opcode[4],
23324           siz_arg2 = (unsigned int)mp.opcode[6];
23325         double *ptr_out = &_mp_arg(1) + (siz_out?1:0);
23326         const double
23327           *ptr_arg1 = siz_arg1==~0U?0:&_mp_arg(3) + (siz_arg1?1:0),
23328           *ptr_arg2 = siz_arg2==~0U?0:&_mp_arg(5) + 1;
23329 
23330         if (!ptr_arg2) { // No filename specified
23331           if (!siz_arg1) return cimg::date((unsigned int)*ptr_arg1);
23332           if (siz_arg1==~0U) for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = k;
23333           else for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
23334           cimg::date(ptr_out,siz_out);
23335           return cimg::type<double>::nan();
23336         }
23337 
23338         // Filename specified.
23339         CImg<charT> ss(siz_arg2 + 1);
23340         cimg_forX(ss,i) ss[i] = (char)ptr_arg2[i];
23341         ss.back() = 0;
23342         if (!siz_arg1) return cimg::fdate(ss,(unsigned int)*ptr_arg1);
23343         for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
23344         cimg::fdate(ss,ptr_out,siz_out);
23345         return cimg::type<double>::nan();
23346       }
23347 
23348       static double mp_debug(_cimg_math_parser& mp) {
23349         CImg<charT> expr(mp.opcode[2] - 4);
23350         {
23351           const ulongT *ptrs = mp.opcode._data + 4;
23352           cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
23353         }
23354         cimg::strellipsize(expr);
23355         const ulongT g_target = mp.opcode[1];
23356 
23357 #if cimg_use_openmp==0
23358         const unsigned int n_thread = 0;
23359 #else
23360         const unsigned int n_thread = omp_get_thread_num();
23361 #endif
23362         cimg_pragma_openmp(critical(mp_debug))
23363         {
23364           std::fprintf(cimg::output(),
23365                        "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23366                        "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)",
23367                        (void*)&mp,n_thread,mp.debug_indent,' ',
23368                        expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width);
23369           std::fflush(cimg::output());
23370           mp.debug_indent+=3;
23371         }
23372         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[3];
23373         CImg<ulongT> _op;
23374         for ( ; mp.p_code<p_end; ++mp.p_code) {
23375           const CImg<ulongT> &op = *mp.p_code;
23376           mp.opcode._data = op._data;
23377 
23378           _op.assign(1,op._height - 1);
23379           const ulongT *ptrs = op._data + 1;
23380           for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd)
23381             *ptrd = *(ptrs++);
23382 
23383           const ulongT target = mp.opcode[1];
23384           mp.mem[target] = _cimg_mp_defunc(mp);
23385           cimg_pragma_openmp(critical(mp_debug))
23386           {
23387             std::fprintf(cimg::output(),
23388                          "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23389                          "Opcode %p = [ %p,%s ] -> mem[%u] = %.17g",
23390                          (void*)&mp,n_thread,mp.debug_indent,' ',
23391                          (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(),
23392                          (unsigned int)target,mp.mem[target]);
23393             std::fflush(cimg::output());
23394           }
23395         }
23396         cimg_pragma_openmp(critical(mp_debug))
23397         {
23398           mp.debug_indent-=3;
23399           std::fprintf(cimg::output(),
23400             "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
23401             "End debugging expression '%s' -> mem[%u] = %.17g (memsize: %u)",
23402             (void*)&mp,n_thread,mp.debug_indent,' ',
23403             expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width);
23404           std::fflush(cimg::output());
23405         }
23406         --mp.p_code;
23407         return mp.mem[g_target];
23408       }
23409 
23410       static double mp_decrement(_cimg_math_parser& mp) {
23411         return _mp_arg(2) - 1;
23412       }
23413 
23414       static double mp_det(_cimg_math_parser& mp) {
23415         const double *ptrs = &_mp_arg(2) + 1;
23416         const unsigned int k = (unsigned int)mp.opcode[3];
23417         return CImg<doubleT>(ptrs,k,k,1,1,true).det();
23418       }
23419 
23420       static double mp_diag(_cimg_math_parser& mp) {
23421         const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3;
23422         double *ptrd = &_mp_arg(1) + 1;
23423         std::memset(ptrd,0,siz*siz*sizeof(double));
23424         for (unsigned int i = 3; i<i_end; ++i) { *(ptrd++) = _mp_arg(i); ptrd+=siz; }
23425         return cimg::type<double>::nan();
23426       }
23427 
23428       static double mp_display_memory(_cimg_math_parser& mp) {
23429         cimg::unused(mp);
23430         std::fputc('\n',cimg::output());
23431         CImg<charT> title(128);
23432         cimg_snprintf(title,title._width,"%s (%u)","[" cimg_appname "_math_parser] Memory snapshot",mp.mem._width);
23433         mp.mem.display(title);
23434         return cimg::type<double>::nan();
23435       }
23436 
23437       static double mp_display(_cimg_math_parser& mp) {
23438         const unsigned int
23439           _siz = (unsigned int)mp.opcode[3],
23440           siz = _siz?_siz:1;
23441         const double *const ptr = &_mp_arg(1) + (_siz?1:0);
23442         const int
23443           w = (int)_mp_arg(4),
23444           h = (int)_mp_arg(5),
23445           d = (int)_mp_arg(6),
23446           s = (int)_mp_arg(7);
23447         CImg<doubleT> img;
23448         if (w>0 && h>0 && d>0 && s>0) {
23449           if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true);
23450           else img.assign(ptr,siz).resize(w,h,d,s,-1);
23451         } else img.assign(ptr,1,siz,1,1,true);
23452 
23453         CImg<charT> expr(mp.opcode[2] - 8);
23454         const ulongT *ptrs = mp.opcode._data + 8;
23455         cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
23456         ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr);
23457         cimg::strellipsize(expr);
23458         std::fputc('\n',cimg::output());
23459         img.display(expr._data);
23460         return cimg::type<double>::nan();
23461       }
23462 
23463       static double mp_div(_cimg_math_parser& mp) {
23464         return _mp_arg(2)/_mp_arg(3);
23465       }
23466 
23467       static double mp_dot(_cimg_math_parser& mp) {
23468         const unsigned int siz = (unsigned int)mp.opcode[4];
23469         return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true).
23470           dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true));
23471       }
23472 
23473       static double mp_do(_cimg_math_parser& mp) {
23474         const ulongT
23475           mem_body = mp.opcode[1],
23476           mem_cond = mp.opcode[2];
23477         const CImg<ulongT>
23478           *const p_body = ++mp.p_code,
23479           *const p_cond = p_body + mp.opcode[3],
23480           *const p_end = p_cond + mp.opcode[4];
23481         const unsigned int vsiz = (unsigned int)mp.opcode[5];
23482         if (mp.opcode[6]) { // Set default value for result and condition if necessary
23483           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
23484           else mp.mem[mem_body] = cimg::type<double>::nan();
23485         }
23486         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
23487 
23488         const unsigned int _break_type = mp.break_type;
23489         mp.break_type = 0;
23490         do {
23491           for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body
23492             mp.opcode._data = mp.p_code->_data;
23493             const ulongT target = mp.opcode[1];
23494             mp.mem[target] = _cimg_mp_defunc(mp);
23495           }
23496           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23497           for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition
23498             mp.opcode._data = mp.p_code->_data;
23499             const ulongT target = mp.opcode[1];
23500             mp.mem[target] = _cimg_mp_defunc(mp);
23501           }
23502           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23503         } while (mp.mem[mem_cond]);
23504         mp.break_type = _break_type;
23505         mp.p_code = p_end - 1;
23506         return mp.mem[mem_body];
23507       }
23508 
23509       static double mp_draw(_cimg_math_parser& mp) {
23510         const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7);
23511         unsigned int ind = (unsigned int)mp.opcode[3];
23512         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
23513         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23514         unsigned int
23515           dx = (unsigned int)mp.opcode[8],
23516           dy = (unsigned int)mp.opcode[9],
23517           dz = (unsigned int)mp.opcode[10],
23518           dc = (unsigned int)mp.opcode[11];
23519         dx = dx==~0U?img._width:(unsigned int)_mp_arg(8);
23520         dy = dy==~0U?img._height:(unsigned int)_mp_arg(9);
23521         dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10);
23522         dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11);
23523 
23524         const ulongT sizS = mp.opcode[2];
23525         if (sizS<(ulongT)dx*dy*dz*dc)
23526           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
23527                                       "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
23528                                       "(%lu values) do not match.",
23529                                       mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
23530         CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true);
23531         const float opacity = (float)_mp_arg(12);
23532 
23533         if (img._data) {
23534           if (mp.opcode[13]!=~0U) { // Opacity mask specified
23535             const ulongT sizM = mp.opcode[14];
23536             if (sizM<(ulongT)dx*dy*dz)
23537               throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
23538                                           "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
23539                                           "(%lu values) do not match.",
23540                                           mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
23541             const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
23542             img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15));
23543           } else img.draw_image(x,y,z,c,S,opacity);
23544         }
23545         return cimg::type<double>::nan();
23546       }
23547 
23548       static double mp_echo(_cimg_math_parser& mp) {
23549         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
23550         CImgList<charT> _str;
23551         CImg<charT> it;
23552         for (unsigned int n = 0; n<nb_args; ++n) {
23553           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
23554           if (siz) { // Vector argument -> string
23555             const double *ptr = &_mp_arg(3 + 2*n) + 1;
23556             unsigned int l = 0;
23557             while (l<siz && ptr[l]) ++l;
23558             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
23559           } else { // Scalar argument -> number
23560             it.assign(24);
23561             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
23562             CImg<charT>::string(it,false,true).move_to(_str);
23563           }
23564         }
23565         CImg(1,1,1,1,0).move_to(_str);
23566         const CImg<charT> str = _str>'x';
23567         std::fprintf(cimg::output(),"\n%s",str._data);
23568         return cimg::type<double>::nan();
23569       }
23570 
23571       static double mp_ellipse(_cimg_math_parser& mp) {
23572         const unsigned int i_end = (unsigned int)mp.opcode[2];
23573         unsigned int ind = (unsigned int)mp.opcode[3];
23574         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
23575         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23576         CImg<T> color(img._spectrum,1,1,1,0);
23577         bool is_invalid_arguments = false, is_outlined = false;
23578         float r1 = 0, r2 = 0, angle = 0, opacity = 1;
23579         unsigned int i = 4, pattern = ~0U;
23580         int x0 = 0, y0 = 0;
23581         if (i>=i_end) is_invalid_arguments = true;
23582         else {
23583           x0 = (int)cimg::round(_mp_arg(i++));
23584           if (i>=i_end) is_invalid_arguments = true;
23585           else {
23586             y0 = (int)cimg::round(_mp_arg(i++));
23587             if (i>=i_end) is_invalid_arguments = true;
23588             else {
23589               r1 = (float)_mp_arg(i++);
23590               if (i>=i_end) r2 = r1;
23591               else {
23592                 r2 = (float)_mp_arg(i++);
23593                 if (i<i_end) {
23594                   angle = (float)_mp_arg(i++);
23595                   if (i<i_end) {
23596                     opacity = (float)_mp_arg(i++);
23597                     if (r1<0 && r2<0) {
23598                       pattern = (unsigned int)_mp_arg(i++);
23599                       is_outlined = true;
23600                       r1 = -r1; r2 = -r2;
23601                     }
23602                     if (i<i_end) {
23603                       cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
23604                       else { color.resize(k,1,1,1,-1); break; }
23605                       color.resize(img._spectrum,1,1,1,0,2);
23606                     }
23607                   }
23608                 }
23609               }
23610             }
23611           }
23612         }
23613         if (!is_invalid_arguments) {
23614           if (is_outlined) img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity,pattern);
23615           else img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity);
23616         } else {
23617           CImg<doubleT> args(i_end - 4);
23618           cimg_forX(args,k) args[k] = _mp_arg(4 + k);
23619           if (ind==~0U)
23620             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
23621                                         "Invalid arguments '%s'. ",
23622                                         mp.imgin.pixel_type(),args.value_string()._data);
23623           else
23624             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
23625                                         "Invalid arguments '#%u%s%s'. ",
23626                                         mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
23627         }
23628         return cimg::type<double>::nan();
23629       }
23630 
23631       static double mp_eq(_cimg_math_parser& mp) {
23632         return (double)(_mp_arg(2)==_mp_arg(3));
23633       }
23634 
23635       static double mp_exp(_cimg_math_parser& mp) {
23636         return std::exp(_mp_arg(2));
23637       }
23638 
23639       static double mp_expr(_cimg_math_parser& mp) {
23640         const unsigned int
23641           sizs = (unsigned int)mp.opcode[3],
23642           w = (unsigned int)mp.opcode[4],
23643           h = (unsigned int)mp.opcode[5],
23644           d = (unsigned int)mp.opcode[6],
23645           s = (unsigned int)mp.opcode[7],
23646           sizd = w*h*d*s;
23647         const double *ptrs = &_mp_arg(2) + 1;
23648         double *ptrd = &_mp_arg(1);
23649         CImg<charT> ss(sizs + 1);
23650         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
23651         ss.back() = 0;
23652         if (!sizd) return CImg<T>(w,h,d,s,0).eval(ss,0,0,0,0,&mp.listin,&mp.listout); // Scalar result
23653         CImg<doubleT>(++ptrd,w,h,d,s,true) = CImg<T>(w,h,d,s,0).fill(ss,true,true,&mp.listin,&mp.listout);
23654         return cimg::type<double>::nan();
23655       }
23656 
23657       static double mp_eye(_cimg_math_parser& mp) {
23658         double *ptrd = &_mp_arg(1) + 1;
23659         const unsigned int k = (unsigned int)mp.opcode[2];
23660         CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix();
23661         return cimg::type<double>::nan();
23662       }
23663 
23664       static double mp_f2ui(_cimg_math_parser& mp) {
23665         return (double)cimg::float2uint((float)_mp_arg(2));
23666       }
23667 
23668       static double mp_factorial(_cimg_math_parser& mp) {
23669         return cimg::factorial((int)_mp_arg(2));
23670       }
23671 
23672       static double mp_fibonacci(_cimg_math_parser& mp) {
23673         return cimg::fibonacci((int)_mp_arg(2));
23674       }
23675 
23676       static double mp_find(_cimg_math_parser& mp) {
23677         const int _step = (int)_mp_arg(6), step = _step?_step:-1;
23678         const ulongT siz = (ulongT)mp.opcode[3];
23679         longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz - 1);
23680         if (ind<0 || ind>=(longT)siz) return -1.;
23681         const double
23682           *const ptrb = &_mp_arg(2) + 1,
23683           *const ptre = ptrb + siz,
23684           val = _mp_arg(4),
23685           *ptr = ptrb + ind;
23686 
23687         // Forward search
23688         if (step>0) {
23689           while (ptr<ptre && *ptr!=val) ptr+=step;
23690           return ptr>=ptre?-1.:(double)(ptr - ptrb);
23691         }
23692 
23693         // Backward search.
23694         while (ptr>=ptrb && *ptr!=val) ptr+=step;
23695         return ptr<ptrb?-1.:(double)(ptr - ptrb);
23696       }
23697 
23698       static double mp_find_seq(_cimg_math_parser& mp) {
23699         const int _step = (int)_mp_arg(7), step = _step?_step:-1;
23700         const ulongT
23701           siz1 = (ulongT)mp.opcode[3],
23702           siz2 = (ulongT)mp.opcode[5];
23703         longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):step>0?0:siz1 - 1);
23704         if (ind<0 || ind>=(longT)siz1) return -1.;
23705         const double
23706           *const ptr1b = &_mp_arg(2) + 1,
23707           *const ptr1e = ptr1b + siz1,
23708           *const ptr2b = &_mp_arg(4) + 1,
23709           *const ptr2e = ptr2b + siz2,
23710           *ptr1 = ptr1b + ind,
23711           *p1 = 0,
23712           *p2 = 0;
23713 
23714         // Forward search.
23715         if (step>0) {
23716           do {
23717             while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
23718             if (ptr1>=ptr1e) return -1.;
23719             p1 = ptr1 + 1;
23720             p2 = ptr2b + 1;
23721             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
23722           } while (p2<ptr2e && (ptr1+=step)<ptr1e);
23723           return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
23724         }
23725 
23726         // Backward search.
23727         do {
23728           while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
23729           if (ptr1<ptr1b) return -1.;
23730           p1 = ptr1 + 1;
23731           p2 = ptr2b + 1;
23732           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
23733         } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
23734         return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
23735       }
23736 
23737       static double mp_floor(_cimg_math_parser& mp) {
23738         return std::floor(_mp_arg(2));
23739       }
23740 
23741       static double mp_for(_cimg_math_parser& mp) {
23742         const ulongT
23743           mem_body = mp.opcode[1],
23744           mem_cond = mp.opcode[3];
23745         const CImg<ulongT>
23746           *const p_init = ++mp.p_code,
23747           *const p_cond = p_init + mp.opcode[4],
23748           *const p_body = p_cond + mp.opcode[5],
23749           *const p_post = p_body + mp.opcode[6],
23750           *const p_end = p_post + mp.opcode[7];
23751         const unsigned int vsiz = (unsigned int)mp.opcode[2];
23752         bool is_cond = false;
23753         if (mp.opcode[8]) { // Set default value for result and condition if necessary
23754           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
23755           else mp.mem[mem_body] = cimg::type<double>::nan();
23756         }
23757         if (mp.opcode[9]) mp.mem[mem_cond] = 0;
23758         const unsigned int _break_type = mp.break_type;
23759         mp.break_type = 0;
23760 
23761         for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
23762           mp.opcode._data = mp.p_code->_data;
23763           const ulongT target = mp.opcode[1];
23764           mp.mem[target] = _cimg_mp_defunc(mp);
23765         }
23766 
23767         if (!mp.break_type) do {
23768             for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
23769               mp.opcode._data = mp.p_code->_data;
23770               const ulongT target = mp.opcode[1];
23771               mp.mem[target] = _cimg_mp_defunc(mp);
23772             }
23773             if (mp.break_type==1) break;
23774 
23775             is_cond = (bool)mp.mem[mem_cond];
23776             if (is_cond && !mp.break_type) {
23777               for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
23778                 mp.opcode._data = mp.p_code->_data;
23779                 const ulongT target = mp.opcode[1];
23780                 mp.mem[target] = _cimg_mp_defunc(mp);
23781               }
23782               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23783 
23784               for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
23785                 mp.opcode._data = mp.p_code->_data;
23786                 const ulongT target = mp.opcode[1];
23787                 mp.mem[target] = _cimg_mp_defunc(mp);
23788               }
23789               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23790             }
23791           } while (is_cond);
23792 
23793         mp.break_type = _break_type;
23794         mp.p_code = p_end - 1;
23795         return mp.mem[mem_body];
23796       }
23797 
23798       static double mp_fsize(_cimg_math_parser& mp) {
23799         const double *ptrs = &_mp_arg(2) + 1;
23800         const ulongT siz = (ulongT)mp.opcode[3];
23801         CImg<charT> ss(siz + 1);
23802         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
23803         ss.back() = 0;
23804         return (double)cimg::fsize(ss);
23805       }
23806 
23807       static double mp_g(_cimg_math_parser& mp) {
23808         cimg::unused(mp);
23809         return cimg::grand(&mp.rng);
23810       }
23811 
23812       static double mp_gauss(_cimg_math_parser& mp) {
23813         const double x = _mp_arg(2), s = _mp_arg(3);
23814         return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1);
23815       }
23816 
23817 #ifdef cimg_mp_func_get
23818       static double mp_get(_cimg_math_parser& mp) {
23819         const double *ptrs = &_mp_arg(2) + 1;
23820         double *ptrd = &_mp_arg(1);
23821         const unsigned int
23822           sizs = (unsigned int)mp.opcode[3],
23823           sizd = (unsigned int)mp.opcode[4];
23824         const bool to_string = (bool)mp.opcode[5];
23825         CImg<charT> ss(sizs + 1);
23826         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
23827         ss.back() = 0;
23828         if (sizd) cimg_mp_func_get(ptrd + 1,sizd,to_string,ss._data);
23829         else cimg_mp_func_get(ptrd,0,to_string,ss._data);
23830         return cimg::type<double>::nan();
23831       }
23832 #endif
23833 
23834       static double mp_gcd(_cimg_math_parser& mp) {
23835         return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3));
23836       }
23837 
23838 #ifdef cimg_mp_func_name
23839       static double mp_name(_cimg_math_parser& mp) {
23840         double *const ptr = &_mp_arg(1) + 1;
23841         const unsigned int siz = (unsigned int)mp.opcode[3];
23842         unsigned int ind = (unsigned int)mp.opcode[2];
23843         if (ind==~0U) std::memset(ptr,0,siz*sizeof(double));
23844         else {
23845           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23846           cimg_mp_func_name(ind,ptr,siz);
23847         }
23848         return cimg::type<double>::nan();
23849       }
23850 #endif
23851 
23852       static double mp_gt(_cimg_math_parser& mp) {
23853         return (double)(_mp_arg(2)>_mp_arg(3));
23854       }
23855 
23856       static double mp_gte(_cimg_math_parser& mp) {
23857         return (double)(_mp_arg(2)>=_mp_arg(3));
23858       }
23859 
23860       static double mp_i(_cimg_math_parser& mp) {
23861         return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y],
23862                                        (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0);
23863       }
23864 
23865       static double mp_if(_cimg_math_parser& mp) {
23866         const bool is_cond = (bool)_mp_arg(2);
23867         const ulongT
23868           mem_left = mp.opcode[3],
23869           mem_right = mp.opcode[4];
23870         const CImg<ulongT>
23871           *const p_right = ++mp.p_code + mp.opcode[5],
23872           *const p_end = p_right + mp.opcode[6];
23873         const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7];
23874         if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) {
23875             mp.opcode._data = mp.p_code->_data;
23876             const ulongT target = mp.opcode[1];
23877             mp.mem[target] = _cimg_mp_defunc(mp);
23878           }
23879         else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) {
23880             mp.opcode._data = mp.p_code->_data;
23881             const ulongT target = mp.opcode[1];
23882             mp.mem[target] = _cimg_mp_defunc(mp);
23883           }
23884         if (mp.p_code==mp.p_break) --mp.p_code;
23885         else mp.p_code = p_end - 1;
23886         if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz);
23887         return mp.mem[is_cond?mem_left:mem_right];
23888       }
23889 
23890       static double mp_image_d(_cimg_math_parser& mp) {
23891         unsigned int ind = (unsigned int)mp.opcode[2];
23892         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23893         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23894         return (double)img.depth();
23895       }
23896 
23897       static double mp_image_display(_cimg_math_parser& mp) {
23898         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
23899         cimg::mutex(6);
23900         CImg<T> &img = mp.listout[ind];
23901         CImg<charT> title(256);
23902         std::fputc('\n',cimg::output());
23903         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
23904         img.display(title);
23905         cimg::mutex(6,0);
23906         return cimg::type<double>::nan();
23907       }
23908 
23909       static double mp_image_h(_cimg_math_parser& mp) {
23910         unsigned int ind = (unsigned int)mp.opcode[2];
23911         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23912         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23913         return (double)img.height();
23914       }
23915 
23916       static double mp_image_median(_cimg_math_parser& mp) {
23917         unsigned int ind = (unsigned int)mp.opcode[2];
23918         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23919         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23920         return (double)img.median();
23921       }
23922 
23923       static double mp_image_norm(_cimg_math_parser& mp) {
23924         unsigned int ind = (unsigned int)mp.opcode[2];
23925         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23926         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23927         return (double)img.magnitude();
23928       }
23929 
23930       static double mp_image_print(_cimg_math_parser& mp) {
23931         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
23932         cimg::mutex(6);
23933         CImg<T> &img = mp.listout[ind];
23934         CImg<charT> title(256);
23935         std::fputc('\n',cimg::output());
23936         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
23937         img.print(title);
23938         cimg::mutex(6,0);
23939         return cimg::type<double>::nan();
23940       }
23941 
23942       static double mp_image_resize(_cimg_math_parser& mp) {
23943         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
23944         cimg::mutex(6);
23945         CImg<T> &img = mp.listout[ind];
23946         const double
23947           _w = mp.opcode[3]==~0U?-100:_mp_arg(3),
23948           _h = mp.opcode[4]==~0U?-100:_mp_arg(4),
23949           _d = mp.opcode[5]==~0U?-100:_mp_arg(5),
23950           _s = mp.opcode[6]==~0U?-100:_mp_arg(6);
23951         const unsigned int
23952           w = (unsigned int)(_w>=0?_w:-_w*img.width()/100),
23953           h = (unsigned int)(_h>=0?_h:-_h*img.height()/100),
23954           d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100),
23955           s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100),
23956           interp = (int)_mp_arg(7);
23957         if (mp.is_fill && img._data==mp.imgout._data) {
23958           cimg::mutex(6,0);
23959           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': "
23960                                       "Cannot both fill and resize image (%u,%u,%u,%u) "
23961                                       "to new dimensions (%u,%u,%u,%u).",
23962                                       img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s);
23963         }
23964         const unsigned int
23965           boundary = (int)_mp_arg(8);
23966         const float
23967           cx = (float)_mp_arg(9),
23968           cy = (float)_mp_arg(10),
23969           cz = (float)_mp_arg(11),
23970           cc = (float)_mp_arg(12);
23971         img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc);
23972         cimg::mutex(6,0);
23973         return cimg::type<double>::nan();
23974       }
23975 
23976       static double mp_image_s(_cimg_math_parser& mp) {
23977         unsigned int ind = (unsigned int)mp.opcode[2];
23978         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
23979         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
23980         return (double)img.spectrum();
23981       }
23982 
23983       static double mp_image_sort(_cimg_math_parser& mp) {
23984         const bool is_increasing = (bool)_mp_arg(3);
23985         const unsigned int
23986           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()),
23987           axis = (unsigned int)_mp_arg(4);
23988         cimg::mutex(6);
23989         CImg<T> &img = mp.listout[ind];
23990         img.sort(is_increasing,
23991                  axis==0 || axis=='x'?'x':
23992                  axis==1 || axis=='y'?'y':
23993                  axis==2 || axis=='z'?'z':
23994                  axis==3 || axis=='c'?'c':0);
23995         cimg::mutex(6,0);
23996         return cimg::type<double>::nan();
23997       }
23998 
23999       static double mp_image_stats(_cimg_math_parser& mp) {
24000         double *ptrd = &_mp_arg(1) + 1;
24001         unsigned int ind = (unsigned int)mp.opcode[2];
24002         if (ind==~0U) CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats();
24003         else {
24004           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24005           CImg<doubleT>(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats();
24006         }
24007         return cimg::type<double>::nan();
24008       }
24009 
24010       static double mp_image_w(_cimg_math_parser& mp) {
24011         unsigned int ind = (unsigned int)mp.opcode[2];
24012         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24013         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24014         return (double)img.width();
24015       }
24016 
24017       static double mp_image_wh(_cimg_math_parser& mp) {
24018         unsigned int ind = (unsigned int)mp.opcode[2];
24019         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24020         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24021         return (double)img.width()*img.height();
24022       }
24023 
24024       static double mp_image_whd(_cimg_math_parser& mp) {
24025         unsigned int ind = (unsigned int)mp.opcode[2];
24026         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24027         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24028         return (double)img.width()*img.height()*img.depth();
24029       }
24030 
24031       static double mp_image_whds(_cimg_math_parser& mp) {
24032         unsigned int ind = (unsigned int)mp.opcode[2];
24033         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24034         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
24035         return (double)img.width()*img.height()*img.depth()*img.spectrum();
24036       }
24037 
24038       static double mp_increment(_cimg_math_parser& mp) {
24039         return _mp_arg(2) + 1;
24040       }
24041 
24042       static double mp_inrange(_cimg_math_parser& mp) {
24043         const unsigned int sizd = (unsigned int)mp.opcode[2];
24044         const bool
24045           include_m = (bool)_mp_arg(9),
24046           include_M = (bool)_mp_arg(10);
24047         if (!sizd) { // Scalar result
24048           const double val = _mp_arg(3);
24049           const double m = _mp_arg(5), M = _mp_arg(7);
24050           if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
24051           else return (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
24052         }
24053 
24054         // Vector result
24055         const unsigned int
24056           siz1 = (unsigned int)mp.opcode[4],
24057           siz2 = (unsigned int)mp.opcode[6],
24058           siz3 = (unsigned int)mp.opcode[8],
24059           off1 = siz1?1:0,
24060           off2 = siz2?1:0,
24061           off3 = siz3?1:0;
24062         double *ptrd = &_mp_arg(1) + 1;
24063         const double
24064           *ptr1 = &_mp_arg(3) + off1,
24065           *ptr2 = &_mp_arg(5) + off2,
24066           *ptr3 = &_mp_arg(7) + off3;
24067         for (unsigned int k = 0; k<sizd; ++k) {
24068           const double val = *ptr1;
24069           const double m = *ptr2, M = *ptr3;
24070           if (M>=m)
24071             ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
24072           else
24073             ptrd[k] = (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
24074           ptr1+=off1;
24075           ptr2+=off2;
24076           ptr3+=off3;
24077         }
24078         return cimg::type<double>::nan();
24079       }
24080 
24081       static double mp_int(_cimg_math_parser& mp) {
24082         return (double)(longT)_mp_arg(2);
24083       }
24084 
24085       static double mp_ioff(_cimg_math_parser& mp) {
24086         const unsigned int
24087           boundary_conditions = (unsigned int)_mp_arg(3);
24088         const CImg<T> &img = mp.imgin;
24089         const longT
24090           off = (longT)_mp_arg(2),
24091           whds = (longT)img.size();
24092         if (off>=0 && off<whds) return (double)img[off];
24093         if (img._data) switch (boundary_conditions) {
24094           case 3 : { // Mirror
24095             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24096             return (double)img[moff<whds?moff:whds2 - moff - 1];
24097           }
24098           case 2 : // Periodic
24099             return (double)img[cimg::mod(off,whds)];
24100           case 1 : // Neumann
24101             return (double)img[off<0?0:whds - 1];
24102           default : // Dirichlet
24103             return 0;
24104           }
24105         return 0;
24106       }
24107 
24108       static double mp_isbool(_cimg_math_parser& mp) {
24109         const double val = _mp_arg(2);
24110         return (double)(val==0. || val==1.);
24111       }
24112 
24113       static double mp_isdir(_cimg_math_parser& mp) {
24114         const double *ptrs = &_mp_arg(2) + 1;
24115         const ulongT siz = (ulongT)mp.opcode[3];
24116         CImg<charT> ss(siz + 1);
24117         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
24118         ss.back() = 0;
24119         return (double)cimg::is_directory(ss);
24120       }
24121 
24122       static double mp_isin(_cimg_math_parser& mp) {
24123         const unsigned int i_end = (unsigned int)mp.opcode[2];
24124         const double val = _mp_arg(3);
24125         for (unsigned int i = 4; i<i_end; ++i)
24126           if (val==_mp_arg(i)) return 1.;
24127         return 0.;
24128       }
24129 
24130       static double mp_isinf(_cimg_math_parser& mp) {
24131         return (double)cimg::type<double>::is_inf(_mp_arg(2));
24132       }
24133 
24134       static double mp_isint(_cimg_math_parser& mp) {
24135         return (double)((double)(longT)_mp_arg(2)==_mp_arg(2));
24136       }
24137 
24138       static double mp_isfile(_cimg_math_parser& mp) {
24139         const double *ptrs = &_mp_arg(2) + 1;
24140         const ulongT siz = (ulongT)mp.opcode[3];
24141         CImg<charT> ss(siz + 1);
24142         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
24143         ss.back() = 0;
24144         return (double)cimg::is_file(ss);
24145       }
24146 
24147       static double mp_isnan(_cimg_math_parser& mp) {
24148         return (double)cimg::type<double>::is_nan(_mp_arg(2));
24149       }
24150 
24151       static double mp_ixyzc(_cimg_math_parser& mp) {
24152         const unsigned int
24153           interpolation = (unsigned int)_mp_arg(6),
24154           boundary_conditions = (unsigned int)_mp_arg(7);
24155         const CImg<T> &img = mp.imgin;
24156         const double
24157           x = _mp_arg(2), y = _mp_arg(3),
24158           z = _mp_arg(4), c = _mp_arg(5);
24159         switch (interpolation) {
24160         case 2 : // Cubic interpolation
24161           switch (boundary_conditions) {
24162           case 3 : { // Mirror
24163             const float
24164               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24165               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24166               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24167             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24168                                             my<img.height()?my:h2 - my - 1,
24169                                             mz<img.depth()?mz:d2 - mz - 1,
24170                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24171           }
24172           case 2 : // Periodic
24173             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24174                                               (int)cimg::mod(c,(double)img._spectrum));
24175           case 1 : // Neumann
24176             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24177                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24178           default : // Dirichlet
24179             if (c<0 || c>=img._spectrum) return (T)0;
24180             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24181           }
24182         case 1 : // Linear interpolation
24183           switch (boundary_conditions) {
24184           case 3 : { // Mirror
24185             const float
24186               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24187               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24188               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24189             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24190                                              my<img.height()?my:h2 - my - 1,
24191                                              mz<img.depth()?mz:d2 - mz - 1,
24192                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24193           }
24194           case 2 : // Periodic
24195             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24196                                                (int)cimg::mod(c,(double)img._spectrum));
24197           case 1 : // Neumann
24198             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24199                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24200           default : // Dirichlet
24201             if (c<0 || c>=img._spectrum) return (T)0;
24202             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24203           }
24204         default : // Nearest neighbor interpolation
24205           switch (boundary_conditions) {
24206           case 3 : { // Mirror
24207             const int
24208               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24209               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24210               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24211             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24212                                my<img.height()?my:h2 - my - 1,
24213                                mz<img.depth()?mz:d2 - mz - 1,
24214                                mc<img.spectrum()?mc:s2 - mc - 1);
24215           }
24216           case 2 : // Periodic
24217             return (double)img((int)cimg::mod(x,(double)img._width),
24218                                (int)cimg::mod(y,(double)img._height),
24219                                (int)cimg::mod(z,(double)img._depth),
24220                                (int)cimg::mod(c,(double)img._spectrum));
24221           case 1 : // Neumann
24222             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24223           default : // Dirichlet
24224             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24225           }
24226         }
24227       }
24228 
24229       static double mp_joff(_cimg_math_parser& mp) {
24230         const unsigned int
24231           boundary_conditions = (unsigned int)_mp_arg(3);
24232         const int
24233           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
24234           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
24235         const CImg<T> &img = mp.imgin;
24236         const longT
24237           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
24238           whds = (longT)img.size();
24239         if (off>=0 && off<whds) return (double)img[off];
24240         if (img._data) switch (boundary_conditions) {
24241           case 3 : { // Mirror
24242             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24243             return (double)img[moff<whds?moff:whds2 - moff - 1];
24244           }
24245           case 2 : // Periodic
24246             return (double)img[cimg::mod(off,whds)];
24247           case 1 : // Neumann
24248             return (double)img[off<0?0:whds - 1];
24249           default : // Dirichlet
24250             return 0;
24251           }
24252         return 0;
24253       }
24254 
24255       static double mp_jxyzc(_cimg_math_parser& mp) {
24256         const unsigned int
24257           interpolation = (unsigned int)_mp_arg(6),
24258           boundary_conditions = (unsigned int)_mp_arg(7);
24259         const CImg<T> &img = mp.imgin;
24260         const double
24261           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
24262           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
24263           x = ox + _mp_arg(2), y = oy + _mp_arg(3),
24264           z = oz + _mp_arg(4), c = oc + _mp_arg(5);
24265         switch (interpolation) {
24266         case 2 : // Cubic interpolation
24267           switch (boundary_conditions) {
24268           case 3 : { // Mirror
24269             const float
24270               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24271               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24272               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24273             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24274                                             my<img.height()?my:h2 - my - 1,
24275                                             mz<img.depth()?mz:d2 - mz - 1,
24276                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24277           }
24278           case 2 : // Periodic
24279             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24280                                               (int)cimg::mod(c,(double)img._spectrum));
24281           case 1 : // Neumann
24282             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24283                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24284           default : // Dirichlet
24285             if (c<0 || c>=img._spectrum) return (T)0;
24286             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24287           }
24288         case 1 : // Linear interpolation
24289           switch (boundary_conditions) {
24290           case 3 : { // Mirror
24291             const float
24292               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24293               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24294               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24295             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24296                                              my<img.height()?my:h2 - my - 1,
24297                                              mz<img.depth()?mz:d2 - mz - 1,
24298                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24299           }
24300           case 2 : // Periodic
24301             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24302                                                (int)cimg::mod(c,(double)img._spectrum));
24303           case 1 : // Neumann
24304             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24305                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24306           default : // Dirichlet
24307             if (c<0 || c>=img._spectrum) return (T)0;
24308             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24309           }
24310         default : // Nearest neighbor interpolation
24311           switch (boundary_conditions) {
24312           case 3 : { // Mirror
24313             const int
24314               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24315               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24316               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24317             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24318                                my<img.height()?my:h2 - my - 1,
24319                                mz<img.depth()?mz:d2 - mz - 1,
24320                                mc<img.spectrum()?mc:s2 - mc - 1);
24321           }
24322           case 2 : // Periodic
24323             return (double)img((int)cimg::mod(x,(double)img._width),
24324                                (int)cimg::mod(y,(double)img._height),
24325                                (int)cimg::mod(z,(double)img._depth),
24326                                (int)cimg::mod(c,(double)img._spectrum));
24327           case 1 : // Neumann
24328             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24329           default : // Dirichlet
24330             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24331           }
24332         }
24333       }
24334 
24335       static double mp_kth(_cimg_math_parser& mp) {
24336         const unsigned int i_end = (unsigned int)mp.opcode[2];
24337         CImg<doubleT> vals(i_end - 4);
24338         double *p = vals.data();
24339         for (unsigned int i = 4; i<i_end; ++i) *(p++) = _mp_arg(i);
24340         longT ind = (longT)cimg::round(_mp_arg(3));
24341         if (ind<0) ind+=vals.width() + 1;
24342         ind = cimg::cut(ind,(longT)1,(longT)vals.width());
24343         return vals.kth_smallest((ulongT)(ind - 1));
24344       }
24345 
24346       static double mp_lerp(_cimg_math_parser& mp) {
24347         const double t = _mp_arg(4);
24348         return _mp_arg(2)*(1-t) + _mp_arg(3)*t;
24349       }
24350 
24351       static double mp_linear_add(_cimg_math_parser& mp) {
24352         return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
24353       }
24354 
24355       static double mp_linear_sub_left(_cimg_math_parser& mp) {
24356         return _mp_arg(2)*_mp_arg(3) - _mp_arg(4);
24357       }
24358 
24359       static double mp_linear_sub_right(_cimg_math_parser& mp) {
24360         return _mp_arg(4) - _mp_arg(2)*_mp_arg(3);
24361       }
24362 
24363       static double mp_list_depth(_cimg_math_parser& mp) {
24364         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24365         return (double)mp.listin[ind]._depth;
24366       }
24367 
24368       static double mp_list_find(_cimg_math_parser& mp) {
24369         const unsigned int
24370           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24371         const CImg<T> &img = mp.listin[indi];
24372         const int _step = (int)_mp_arg(5), step = _step?_step:-1;
24373         const ulongT siz = (ulongT)img.size();
24374         longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1);
24375         if (ind<0 || ind>=(longT)siz) return -1.;
24376         const T
24377           *const ptrb = img.data(),
24378           *const ptre = img.end(),
24379           *ptr = ptrb + ind;
24380         const double val = _mp_arg(3);
24381 
24382         // Forward search
24383         if (step>0) {
24384           while (ptr<ptre && (double)*ptr!=val) ptr+=step;
24385           return ptr>=ptre?-1.:(double)(ptr - ptrb);
24386         }
24387 
24388         // Backward search.
24389         while (ptr>=ptrb && (double)*ptr!=val) ptr+=step;
24390         return ptr<ptrb?-1.:(double)(ptr - ptrb);
24391       }
24392 
24393       static double mp_list_find_seq(_cimg_math_parser& mp) {
24394         const unsigned int
24395           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24396         const CImg<T> &img = mp.listin[indi];
24397         const int _step = (bool)_mp_arg(6), step = _step?_step:-1;
24398         const ulongT
24399           siz1 = (ulongT)img.size(),
24400           siz2 = (ulongT)mp.opcode[4];
24401         longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz1 - 1);
24402         if (ind<0 || ind>=(longT)siz1) return -1.;
24403         const T
24404           *const ptr1b = img.data(),
24405           *const ptr1e = ptr1b + siz1,
24406           *ptr1 = ptr1b + ind,
24407           *p1 = 0;
24408         const double
24409           *const ptr2b = &_mp_arg(3) + 1,
24410           *const ptr2e = ptr2b + siz2,
24411           *p2 = 0;
24412 
24413         // Forward search.
24414         if (step>0) {
24415           do {
24416             while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
24417             if (ptr1>=ptr1e) return -1.;
24418             p1 = ptr1 + 1;
24419             p2 = ptr2b + 1;
24420             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
24421           } while (p2<ptr2e && (ptr1+=step)<ptr1e);
24422           return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
24423         }
24424 
24425         // Backward search.
24426         do {
24427           while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
24428           if (ptr1<ptr1b) return -1.;
24429           p1 = ptr1 + 1;
24430           p2 = ptr2b + 1;
24431           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
24432         } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
24433         return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
24434       }
24435 
24436       static double mp_list_height(_cimg_math_parser& mp) {
24437         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24438         return (double)mp.listin[ind]._height;
24439       }
24440 
24441       static double mp_list_ioff(_cimg_math_parser& mp) {
24442         const unsigned int
24443           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24444           boundary_conditions = (unsigned int)_mp_arg(4);
24445         const CImg<T> &img = mp.listin[ind];
24446         const longT
24447           off = (longT)_mp_arg(3),
24448           whds = (longT)img.size();
24449         if (off>=0 && off<whds) return (double)img[off];
24450         if (img._data) switch (boundary_conditions) {
24451           case 3 : { // Mirror
24452             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24453             return (double)img[moff<whds?moff:whds2 - moff - 1];
24454           }
24455           case 2 : // Periodic
24456             return (double)img[cimg::mod(off,whds)];
24457           case 1 : // Neumann
24458             return (double)img[off<0?0:whds - 1];
24459           default : // Dirichlet
24460             return 0;
24461           }
24462         return 0;
24463       }
24464 
24465       static double mp_list_is_shared(_cimg_math_parser& mp) {
24466         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24467         return (double)mp.listin[ind]._is_shared;
24468       }
24469 
24470       static double mp_list_ixyzc(_cimg_math_parser& mp) {
24471         const unsigned int
24472           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24473           interpolation = (unsigned int)_mp_arg(7),
24474           boundary_conditions = (unsigned int)_mp_arg(8);
24475         const CImg<T> &img = mp.listin[ind];
24476         const double
24477           x = _mp_arg(3), y = _mp_arg(4),
24478           z = _mp_arg(5), c = _mp_arg(6);
24479         switch (interpolation) {
24480         case 2 : // Cubic interpolation
24481           switch (boundary_conditions) {
24482           case 3 : { // Mirror
24483             const float
24484               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24485               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24486               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24487             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24488                                             my<img.height()?my:h2 - my - 1,
24489                                             mz<img.depth()?mz:d2 - mz - 1,
24490                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24491           }
24492           case 2 : // Periodic
24493             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24494                                               (int)cimg::mod(c,(double)img._spectrum));
24495           case 1 : // Neumann
24496             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24497                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24498           default : // Dirichlet
24499             if (c<0 || c>=img._spectrum) return (T)0;
24500             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24501           }
24502         case 1 : // Linear interpolation
24503           switch (boundary_conditions) {
24504           case 3 : { // Mirror
24505             const float
24506               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24507               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24508               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24509             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24510                                              my<img.height()?my:h2 - my - 1,
24511                                              mz<img.depth()?mz:d2 - mz - 1,
24512                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24513           }
24514           case 2 : // Periodic
24515             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24516                                                (int)cimg::mod(c,(double)img._spectrum));
24517           case 1 : // Neumann
24518             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24519                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24520           default : // Dirichlet
24521             if (c<0 || c>=img._spectrum) return (T)0;
24522             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24523           }
24524         default : // Nearest neighbor interpolation
24525           switch (boundary_conditions) {
24526           case 3 : { // Mirror
24527             const int
24528               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24529               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24530               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24531             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24532                                my<img.height()?my:h2 - my - 1,
24533                                mz<img.depth()?mz:d2 - mz - 1,
24534                                mc<img.spectrum()?mc:s2 - mc - 1);
24535           }
24536           case 2 : // Periodic
24537             return (double)img((int)cimg::mod(x,(double)img._width),
24538                                (int)cimg::mod(y,(double)img._height),
24539                                (int)cimg::mod(z,(double)img._depth),
24540                                (int)cimg::mod(c,(double)img._spectrum));
24541           case 1 : // Neumann
24542             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24543           default : // Dirichlet
24544             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24545           }
24546         }
24547       }
24548 
24549       static double mp_list_joff(_cimg_math_parser& mp) {
24550         const unsigned int
24551           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24552           boundary_conditions = (unsigned int)_mp_arg(4);
24553         const int
24554           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
24555           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
24556         const CImg<T> &img = mp.listin[ind];
24557         const longT
24558           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
24559           whds = (longT)img.size();
24560         if (off>=0 && off<whds) return (double)img[off];
24561         if (img._data) switch (boundary_conditions) {
24562           case 3 : { // Mirror
24563             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
24564             return (double)img[moff<whds?moff:whds2 - moff - 1];
24565           }
24566           case 2 : // Periodic
24567             return (double)img[cimg::mod(off,whds)];
24568           case 1 : // Neumann
24569             return (double)img[off<0?0:whds - 1];
24570           default : // Dirichlet
24571             return 0;
24572           }
24573         return 0;
24574       }
24575 
24576       static double mp_list_jxyzc(_cimg_math_parser& mp) {
24577         const unsigned int
24578           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24579           interpolation = (unsigned int)_mp_arg(7),
24580           boundary_conditions = (unsigned int)_mp_arg(8);
24581         const CImg<T> &img = mp.listin[ind];
24582         const double
24583           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
24584           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
24585           x = ox + _mp_arg(3), y = oy + _mp_arg(4),
24586           z = oz + _mp_arg(5), c = oc + _mp_arg(6);
24587         switch (interpolation) {
24588         case 2 : // Cubic interpolation
24589           switch (boundary_conditions) {
24590           case 3 : { // Mirror
24591             const float
24592               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24593               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24594               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24595             return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24596                                             my<img.height()?my:h2 - my - 1,
24597                                             mz<img.depth()?mz:d2 - mz - 1,
24598                                             (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24599           }
24600           case 2 : // Periodic
24601             return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
24602                                               (int)cimg::mod(c,(double)img._spectrum));
24603           case 1 : // Neumann
24604             return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
24605                                             (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24606           default : // Dirichlet
24607             if (c<0 || c>=img._spectrum) return (T)0;
24608             return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24609           }
24610         case 1 : // Linear interpolation
24611           switch (boundary_conditions) {
24612           case 3 : { // Mirror
24613             const float
24614               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
24615               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
24616               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
24617             return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
24618                                              my<img.height()?my:h2 - my - 1,
24619                                              mz<img.depth()?mz:d2 - mz - 1,
24620                                              (int)(mc<img.spectrum()?mc:s2 - mc - 1));
24621           }
24622           case 2 : // Periodic
24623             return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
24624                                                (int)cimg::mod(c,(double)img._spectrum));
24625           case 1 : // Neumann
24626             return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
24627                                              (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
24628           default : // Dirichlet
24629             if (c<0 || c>=img._spectrum) return (T)0;
24630             return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
24631           }
24632         default : // Nearest neighbor interpolation
24633           switch (boundary_conditions) {
24634           case 3 : { // Mirror
24635             const int
24636               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
24637               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
24638               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
24639             return (double)img(mx<img.width()?mx:w2 - mx - 1,
24640                                my<img.height()?my:h2 - my - 1,
24641                                mz<img.depth()?mz:d2 - mz - 1,
24642                                mc<img.spectrum()?mc:s2 - mc - 1);
24643           }
24644           case 2 : // Periodic
24645             return (double)img((int)cimg::mod(x,(double)img._width),
24646                                (int)cimg::mod(y,(double)img._height),
24647                                (int)cimg::mod(z,(double)img._depth),
24648                                (int)cimg::mod(c,(double)img._spectrum));
24649           case 1 : // Neumann
24650             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
24651           default : // Dirichlet
24652             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
24653           }
24654         }
24655       }
24656 
24657       static double mp_list_l(_cimg_math_parser& mp) {
24658         return (double)mp.listout.width();
24659       }
24660 
24661       static double mp_list_median(_cimg_math_parser& mp) {
24662         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24663         if (!mp.list_median) mp.list_median.assign(mp.listin._width);
24664         if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]);
24665         return *mp.list_median[ind];
24666       }
24667 
24668       static double mp_list_norm(_cimg_math_parser& mp) {
24669         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24670         if (!mp.list_norm) mp.list_norm.assign(mp.listin._width);
24671         if (!mp.list_norm[ind]) CImg<doubleT>::vector(mp.listin[ind].magnitude()).move_to(mp.list_norm[ind]);
24672         return *mp.list_norm[ind];
24673       }
24674 
24675       static double mp_list_set_ioff(_cimg_math_parser& mp) {
24676         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24677         CImg<T> &img = mp.listout[ind];
24678         const longT
24679           off = (longT)_mp_arg(3),
24680           whds = (longT)img.size();
24681         const double val = _mp_arg(1);
24682         if (off>=0 && off<whds) img[off] = (T)val;
24683         return val;
24684       }
24685 
24686       static double mp_list_set_ixyzc(_cimg_math_parser& mp) {
24687         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24688         CImg<T> &img = mp.listout[ind];
24689         const int
24690           x = (int)_mp_arg(3), y = (int)_mp_arg(4),
24691           z = (int)_mp_arg(5), c = (int)_mp_arg(6);
24692         const double val = _mp_arg(1);
24693         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
24694             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
24695           img(x,y,z,c) = (T)val;
24696         return val;
24697       }
24698 
24699       static double mp_list_set_joff(_cimg_math_parser& mp) {
24700         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24701         CImg<T> &img = mp.listout[ind];
24702         const int
24703           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
24704           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
24705         const longT
24706           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
24707           whds = (longT)img.size();
24708         const double val = _mp_arg(1);
24709         if (off>=0 && off<whds) img[off] = (T)val;
24710         return val;
24711       }
24712 
24713       static double mp_list_set_jxyzc(_cimg_math_parser& mp) {
24714         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24715         CImg<T> &img = mp.listout[ind];
24716         const double
24717           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
24718           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
24719         const int
24720           x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)),
24721           z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6));
24722         const double val = _mp_arg(1);
24723         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
24724             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
24725           img(x,y,z,c) = (T)val;
24726         return val;
24727       }
24728 
24729       static double mp_list_set_Ioff_s(_cimg_math_parser& mp) {
24730         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24731         CImg<T> &img = mp.listout[ind];
24732         const longT
24733           off = (longT)_mp_arg(3),
24734           whd = (longT)img.width()*img.height()*img.depth();
24735         const T val = (T)_mp_arg(1);
24736         if (off>=0 && off<whd) {
24737           T *ptrd = &img[off];
24738           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
24739         }
24740         return _mp_arg(1);
24741       }
24742 
24743       static double mp_list_set_Ioff_v(_cimg_math_parser& mp) {
24744         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24745         CImg<T> &img = mp.listout[ind];
24746         const longT
24747           off = (longT)_mp_arg(3),
24748           whd = (longT)img.width()*img.height()*img.depth();
24749         const double *ptrs = &_mp_arg(1) + 1;
24750         if (off>=0 && off<whd) {
24751           const unsigned int vsiz = (unsigned int)mp.opcode[4];
24752           T *ptrd = &img[off];
24753           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
24754         }
24755         return cimg::type<double>::nan();
24756       }
24757 
24758       static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) {
24759         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24760         CImg<T> &img = mp.listout[ind];
24761         const int
24762           x = (int)_mp_arg(3),
24763           y = (int)_mp_arg(4),
24764           z = (int)_mp_arg(5);
24765         const T val = (T)_mp_arg(1);
24766         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
24767           T *ptrd = &img(x,y,z);
24768           const ulongT whd = (ulongT)img._width*img._height*img._depth;
24769           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
24770         }
24771         return _mp_arg(1);
24772       }
24773 
24774       static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) {
24775         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24776         CImg<T> &img = mp.listout[ind];
24777         const int
24778           x = (int)_mp_arg(3),
24779           y = (int)_mp_arg(4),
24780           z = (int)_mp_arg(5);
24781         const double *ptrs = &_mp_arg(1) + 1;
24782         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
24783           const unsigned int vsiz = (unsigned int)mp.opcode[6];
24784           T *ptrd = &img(x,y,z);
24785           const ulongT whd = (ulongT)img._width*img._height*img._depth;
24786           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
24787         }
24788         return cimg::type<double>::nan();
24789       }
24790 
24791       static double mp_list_set_Joff_s(_cimg_math_parser& mp) {
24792         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24793         CImg<T> &img = mp.listout[ind];
24794         const int
24795           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
24796           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
24797         const longT
24798           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
24799           whd = (longT)img.width()*img.height()*img.depth();
24800         const T val = (T)_mp_arg(1);
24801         if (off>=0 && off<whd) {
24802           T *ptrd = &img[off];
24803           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
24804         }
24805         return _mp_arg(1);
24806       }
24807 
24808       static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
24809         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24810         CImg<T> &img = mp.listout[ind];
24811         const int
24812           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
24813           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
24814         const longT
24815           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
24816           whd = (longT)img.width()*img.height()*img.depth();
24817         const double *ptrs = &_mp_arg(1) + 1;
24818         if (off>=0 && off<whd) {
24819           const unsigned int vsiz = (unsigned int)mp.opcode[4];
24820           T *ptrd = &img[off];
24821           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
24822         }
24823         return cimg::type<double>::nan();
24824       }
24825 
24826       static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) {
24827         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24828         CImg<T> &img = mp.listout[ind];
24829         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
24830         const int
24831           x = (int)(ox + _mp_arg(3)),
24832           y = (int)(oy + _mp_arg(4)),
24833           z = (int)(oz + _mp_arg(5));
24834         const T val = (T)_mp_arg(1);
24835         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
24836           T *ptrd = &img(x,y,z);
24837           const ulongT whd = (ulongT)img._width*img._height*img._depth;
24838           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
24839         }
24840         return _mp_arg(1);
24841       }
24842 
24843       static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) {
24844         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24845         CImg<T> &img = mp.listout[ind];
24846         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
24847         const int
24848           x = (int)(ox + _mp_arg(3)),
24849           y = (int)(oy + _mp_arg(4)),
24850           z = (int)(oz + _mp_arg(5));
24851         const double *ptrs = &_mp_arg(1) + 1;
24852         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
24853           const unsigned int vsiz = (unsigned int)mp.opcode[6];
24854           T *ptrd = &img(x,y,z);
24855           const ulongT whd = (ulongT)img._width*img._height*img._depth;
24856           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
24857         }
24858         return cimg::type<double>::nan();
24859       }
24860 
24861       static double mp_list_spectrum(_cimg_math_parser& mp) {
24862         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24863         return (double)mp.listin[ind]._spectrum;
24864       }
24865 
24866       static double mp_list_stats(_cimg_math_parser& mp) {
24867         const unsigned int
24868           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24869           k = (unsigned int)mp.opcode[3];
24870         if (!mp.list_stats) mp.list_stats.assign(mp.listin._width);
24871         if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false);
24872         return mp.list_stats(ind,k);
24873       }
24874 
24875       static double mp_list_wh(_cimg_math_parser& mp) {
24876         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24877         return (double)mp.listin[ind]._width*mp.listin[ind]._height;
24878       }
24879 
24880       static double mp_list_whd(_cimg_math_parser& mp) {
24881         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24882         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth;
24883       }
24884 
24885       static double mp_list_whds(_cimg_math_parser& mp) {
24886         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24887         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum;
24888       }
24889 
24890       static double mp_list_width(_cimg_math_parser& mp) {
24891         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
24892         return (double)mp.listin[ind]._width;
24893       }
24894 
24895       static double mp_list_Ioff(_cimg_math_parser& mp) {
24896         double *ptrd = &_mp_arg(1) + 1;
24897         const unsigned int
24898           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24899           boundary_conditions = (unsigned int)_mp_arg(4),
24900           vsiz = (unsigned int)mp.opcode[5];
24901         const CImg<T> &img = mp.listin[ind];
24902         const longT
24903           off = (longT)_mp_arg(3),
24904           whd = (longT)img.width()*img.height()*img.depth();
24905         const T *ptrs;
24906         if (off>=0 && off<whd) {
24907           ptrs = &img[off];
24908           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
24909           return cimg::type<double>::nan();
24910         }
24911         if (img._data) switch (boundary_conditions) {
24912           case 3 : { // Mirror
24913             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
24914             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
24915             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
24916             return cimg::type<double>::nan();
24917           }
24918           case 2 : // Periodic
24919             ptrs = &img[cimg::mod(off,whd)];
24920             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
24921             return cimg::type<double>::nan();
24922           case 1 : // Neumann
24923             ptrs = off<0?&img[0]:&img[whd - 1];
24924             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
24925             return cimg::type<double>::nan();
24926           default : // Dirichlet
24927             std::memset(ptrd,0,vsiz*sizeof(double));
24928             return cimg::type<double>::nan();
24929           }
24930         std::memset(ptrd,0,vsiz*sizeof(double));
24931         return cimg::type<double>::nan();
24932       }
24933 
24934       static double mp_list_Ixyz(_cimg_math_parser& mp) {
24935         double *ptrd = &_mp_arg(1) + 1;
24936         const unsigned int
24937           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
24938           interpolation = (unsigned int)_mp_arg(6),
24939           boundary_conditions = (unsigned int)_mp_arg(7),
24940           vsiz = (unsigned int)mp.opcode[8];
24941         const CImg<T> &img = mp.listin[ind];
24942         const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5);
24943         const ulongT whd = (ulongT)img._width*img._height*img._depth;
24944         const T *ptrs;
24945         switch (interpolation) {
24946         case 2 : // Cubic interpolation
24947           switch (boundary_conditions) {
24948           case 3 : { // Mirror
24949             const float
24950               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
24951               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
24952               cx = mx<img.width()?mx:w2 - mx - 1,
24953               cy = my<img.height()?my:h2 - my - 1,
24954               cz = mz<img.depth()?mz:d2 - mz - 1;
24955             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
24956           } break;
24957           case 2 : // Periodic
24958             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
24959             break;
24960           case 1 : // Neumann
24961             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
24962             break;
24963           default : // Dirichlet
24964             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
24965           } break;
24966         case 1 : // Linear interpolation
24967           switch (boundary_conditions) {
24968           case 3 : { // Mirror
24969             const float
24970               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
24971               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
24972               cx = mx<img.width()?mx:w2 - mx - 1,
24973               cy = my<img.height()?my:h2 - my - 1,
24974               cz = mz<img.depth()?mz:d2 - mz - 1;
24975             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
24976           } break;
24977           case 2 : // Periodic
24978             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
24979             break;
24980           case 1 : // Neumann
24981             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
24982             break;
24983           default : // Dirichlet
24984             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
24985           } break;
24986         default : // Nearest neighbor interpolation
24987           switch (boundary_conditions) {
24988           case 3 : { // Mirror
24989             const int
24990               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
24991               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
24992               cx = mx<img.width()?mx:w2 - mx - 1,
24993               cy = my<img.height()?my:h2 - my - 1,
24994               cz = mz<img.depth()?mz:d2 - mz - 1;
24995             ptrs = &img(cx,cy,cz);
24996             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
24997           } break;
24998           case 2 : { // Periodic
24999             const int
25000               cx = (int)cimg::mod(x,(double)img._width),
25001               cy = (int)cimg::mod(y,(double)img._height),
25002               cz = (int)cimg::mod(z,(double)img._depth);
25003             ptrs = &img(cx,cy,cz);
25004             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25005           } break;
25006           case 1 : { // Neumann
25007             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
25008             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25009           } break;
25010           default : // Dirichlet
25011             if (img.containsXYZC((int)x,(int)y,(int)z)) {
25012               ptrs = &img((int)x,(int)y,(int)z);
25013               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25014             } else std::memset(ptrd,0,vsiz*sizeof(double));
25015           }
25016         }
25017         return cimg::type<double>::nan();
25018       }
25019 
25020       static double mp_list_Joff(_cimg_math_parser& mp) {
25021         double *ptrd = &_mp_arg(1) + 1;
25022         const unsigned int
25023           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25024           boundary_conditions = (unsigned int)_mp_arg(4),
25025           vsiz = (unsigned int)mp.opcode[5];
25026         const int
25027           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z];
25028         const CImg<T> &img = mp.listin[ind];
25029         const longT
25030           off = img.offset(ox,oy,oz) + (longT)_mp_arg(3),
25031           whd = (longT)img.width()*img.height()*img.depth();
25032         const T *ptrs;
25033         if (off>=0 && off<whd) {
25034           ptrs = &img[off];
25035           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25036           return cimg::type<double>::nan();
25037         }
25038         if (img._data) switch (boundary_conditions) {
25039           case 3 : { // Mirror
25040             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
25041             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
25042             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25043             return cimg::type<double>::nan();
25044           }
25045           case 2 : // Periodic
25046             ptrs = &img[cimg::mod(off,whd)];
25047             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25048             return cimg::type<double>::nan();
25049           case 1 : // Neumann
25050             ptrs = off<0?&img[0]:&img[whd - 1];
25051             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
25052             return cimg::type<double>::nan();
25053           default : // Dirichlet
25054             std::memset(ptrd,0,vsiz*sizeof(double));
25055             return cimg::type<double>::nan();
25056           }
25057         std::memset(ptrd,0,vsiz*sizeof(double));
25058         return cimg::type<double>::nan();
25059       }
25060 
25061       static double mp_list_Jxyz(_cimg_math_parser& mp) {
25062         double *ptrd = &_mp_arg(1) + 1;
25063         const unsigned int
25064           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
25065           interpolation = (unsigned int)_mp_arg(6),
25066           boundary_conditions = (unsigned int)_mp_arg(7),
25067           vsiz = (unsigned int)mp.opcode[8];
25068         const CImg<T> &img = mp.listin[ind];
25069         const double
25070           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
25071           x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
25072         const ulongT whd = (ulongT)img._width*img._height*img._depth;
25073         const T *ptrs;
25074         switch (interpolation) {
25075         case 2 : // Cubic interpolation
25076           switch (boundary_conditions) {
25077           case 3 : { // Mirror
25078             const float
25079               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25080               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25081               cx = mx<img.width()?mx:w2 - mx - 1,
25082               cy = my<img.height()?my:h2 - my - 1,
25083               cz = mz<img.depth()?mz:d2 - mz - 1;
25084             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
25085           } break;
25086           case 2 : // Periodic
25087             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
25088             break;
25089           case 1 : // Neumann
25090             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
25091             break;
25092           default : // Dirichlet
25093             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25094           } break;
25095         case 1 : // Linear interpolation
25096           switch (boundary_conditions) {
25097           case 3 : { // Mirror
25098             const float
25099               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
25100               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
25101               cx = mx<img.width()?mx:w2 - mx - 1,
25102               cy = my<img.height()?my:h2 - my - 1,
25103               cz = mz<img.depth()?mz:d2 - mz - 1;
25104             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
25105           } break;
25106           case 2 : // Periodic
25107             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
25108             break;
25109           case 1 : // Neumann
25110             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
25111             break;
25112           default : // Dirichlet
25113             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
25114           } break;
25115         case 0 : // Nearest neighbor interpolation
25116           switch (boundary_conditions) {
25117           case 3 : { // Mirror
25118             const int
25119               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
25120               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
25121               cx = mx<img.width()?mx:w2 - mx - 1,
25122               cy = my<img.height()?my:h2 - my - 1,
25123               cz = mz<img.depth()?mz:d2 - mz - 1;
25124             ptrs = &img(cx,cy,cz);
25125             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25126           } break;
25127           case 2 : { // Periodic
25128             const int
25129               cx = (int)cimg::mod(x,(double)img._width),
25130               cy = (int)cimg::mod(y,(double)img._height),
25131               cz = (int)cimg::mod(z,(double)img._depth);
25132             ptrs = &img(cx,cy,cz);
25133             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25134           } break;
25135           case 1 : { // Neumann
25136             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
25137             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25138           } break;
25139           default : // Dirichlet
25140             if (img.containsXYZC((int)x,(int)y,(int)z)) {
25141               ptrs = &img((int)x,(int)y,(int)z);
25142               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
25143             } else std::memset(ptrd,0,vsiz*sizeof(double));
25144           }
25145         }
25146         return cimg::type<double>::nan();
25147       }
25148 
25149       static double mp_log(_cimg_math_parser& mp) {
25150         return std::log(_mp_arg(2));
25151       }
25152 
25153       static double mp_log10(_cimg_math_parser& mp) {
25154         return std::log10(_mp_arg(2));
25155       }
25156 
25157       static double mp_log2(_cimg_math_parser& mp) {
25158         return cimg::log2(_mp_arg(2));
25159       }
25160 
25161       static double mp_logical_and(_cimg_math_parser& mp) {
25162         const bool val_left = (bool)_mp_arg(2);
25163         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
25164         if (!val_left) { mp.p_code = p_end - 1; return 0; }
25165         const ulongT mem_right = mp.opcode[3];
25166         for ( ; mp.p_code<p_end; ++mp.p_code) {
25167           mp.opcode._data = mp.p_code->_data;
25168           const ulongT target = mp.opcode[1];
25169           mp.mem[target] = _cimg_mp_defunc(mp);
25170         }
25171         --mp.p_code;
25172         return (double)(bool)mp.mem[mem_right];
25173       }
25174 
25175       static double mp_logical_not(_cimg_math_parser& mp) {
25176         return (double)!_mp_arg(2);
25177       }
25178 
25179       static double mp_logical_or(_cimg_math_parser& mp) {
25180         const bool val_left = (bool)_mp_arg(2);
25181         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
25182         if (val_left) { mp.p_code = p_end - 1; return 1; }
25183         const ulongT mem_right = mp.opcode[3];
25184         for ( ; mp.p_code<p_end; ++mp.p_code) {
25185           mp.opcode._data = mp.p_code->_data;
25186           const ulongT target = mp.opcode[1];
25187           mp.mem[target] = _cimg_mp_defunc(mp);
25188         }
25189         --mp.p_code;
25190         return (double)(bool)mp.mem[mem_right];
25191       }
25192 
25193       static double mp_lowercase(_cimg_math_parser& mp) {
25194         return cimg::lowercase(_mp_arg(2));
25195       }
25196 
25197       static double mp_lt(_cimg_math_parser& mp) {
25198         return (double)(_mp_arg(2)<_mp_arg(3));
25199       }
25200 
25201       static double mp_lte(_cimg_math_parser& mp) {
25202         return (double)(_mp_arg(2)<=_mp_arg(3));
25203       }
25204 
25205       static double mp_matrix_eig(_cimg_math_parser& mp) {
25206         double *ptrd = &_mp_arg(1) + 1;
25207         const double *ptr1 = &_mp_arg(2) + 1;
25208         const unsigned int k = (unsigned int)mp.opcode[3];
25209         CImg<doubleT> val, vec;
25210         CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec);
25211         CImg<doubleT>(ptrd,1,k,1,1,true) = val;
25212         CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose();
25213         return cimg::type<double>::nan();
25214       }
25215 
25216       static double mp_matrix_invert(_cimg_math_parser& mp) {
25217         double *const ptrd = &_mp_arg(1) + 1;
25218         const double *const ptr1 = &_mp_arg(2) + 1;
25219         const unsigned int k = (unsigned int)mp.opcode[3];
25220         const bool use_LU = (bool)_mp_arg(4);
25221         CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptr1,k,k,1,1,true).get_invert(use_LU);
25222         return cimg::type<double>::nan();
25223       }
25224 
25225       static double mp_matrix_mul(_cimg_math_parser& mp) {
25226         double *ptrd = &_mp_arg(1) + 1;
25227         const double
25228           *ptr1 = &_mp_arg(2) + 1,
25229           *ptr2 = &_mp_arg(3) + 1;
25230         const unsigned int
25231           k = (unsigned int)mp.opcode[4],
25232           l = (unsigned int)mp.opcode[5],
25233           m = (unsigned int)mp.opcode[6];
25234         CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true);
25235         return cimg::type<double>::nan();
25236       }
25237 
25238       static double mp_matrix_pseudoinvert(_cimg_math_parser& mp) {
25239         double *ptrd = &_mp_arg(1) + 1;
25240         const double *ptr1 = &_mp_arg(2) + 1;
25241         const unsigned int
25242           k = (unsigned int)mp.opcode[3],
25243           l = (unsigned int)mp.opcode[4];
25244         const bool use_LU = (bool)_mp_arg(5);
25245         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptr1,k,l,1,1,true).get_pseudoinvert(use_LU);
25246         return cimg::type<double>::nan();
25247       }
25248 
25249       static double mp_matrix_svd(_cimg_math_parser& mp) {
25250         double *ptrd = &_mp_arg(1) + 1;
25251         const double *ptr1 = &_mp_arg(2) + 1;
25252         const unsigned int
25253           k = (unsigned int)mp.opcode[3],
25254           l = (unsigned int)mp.opcode[4];
25255         CImg<doubleT> U, S, V;
25256         CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V);
25257         CImg<doubleT>(ptrd,k,l,1,1,true) = U;
25258         CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S;
25259         CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V;
25260         return cimg::type<double>::nan();
25261       }
25262 
25263       static double mp_max(_cimg_math_parser& mp) {
25264         const unsigned int i_end = (unsigned int)mp.opcode[2];
25265         double val = _mp_arg(3);
25266         for (unsigned int i = 4; i<i_end; ++i) val = std::max(val,_mp_arg(i));
25267         return val;
25268       }
25269 
25270       static double mp_maxabs(_cimg_math_parser& mp) {
25271         const unsigned int i_end = (unsigned int)mp.opcode[2];
25272         double val = _mp_arg(3), absval = cimg::abs(val);
25273         for (unsigned int i = 4; i<i_end; ++i) {
25274           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
25275           if (_absval>absval) { val = _val; absval = _absval; }
25276         }
25277         return val;
25278       }
25279 
25280       static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
25281                                         const longT siz, const long inc) {
25282         const longT
25283           off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind,
25284           eoff = off + (siz - 1)*inc;
25285         if (off<0 || eoff>=mp.mem.width())
25286           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
25287                                       "Out-of-bounds variable pointer "
25288                                       "(length: %ld, increment: %ld, offset start: %ld, "
25289                                       "offset end: %ld, offset max: %u).",
25290                                       mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1);
25291         return &mp.mem[off];
25292       }
25293 
25294       static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref,
25295                                       const longT siz, const long inc, const bool is_out) {
25296         const unsigned ind = (unsigned int)p_ref[1];
25297         const CImg<T> &img = is_out?
25298           (ind==~0U?mp.imgout:mp.listout[cimg::mod((int)mp.mem[ind],mp.listout.width())]):
25299           (ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]);
25300         const bool is_relative = (bool)p_ref[2];
25301         int ox, oy, oz, oc;
25302         longT off = 0;
25303         if (is_relative) {
25304           ox = (int)mp.mem[_cimg_mp_slot_x];
25305           oy = (int)mp.mem[_cimg_mp_slot_y];
25306           oz = (int)mp.mem[_cimg_mp_slot_z];
25307           oc = (int)mp.mem[_cimg_mp_slot_c];
25308           off = img.offset(ox,oy,oz,oc);
25309         }
25310         if ((*p_ref)%2) {
25311           const int
25312             x = (int)mp.mem[p_ref[3]],
25313             y = (int)mp.mem[p_ref[4]],
25314             z = (int)mp.mem[p_ref[5]],
25315             c = *p_ref==5?0:(int)mp.mem[p_ref[6]];
25316           off+=img.offset(x,y,z,c);
25317         } else off+=(longT)mp.mem[p_ref[3]];
25318         const longT eoff = off + (siz - 1)*inc;
25319         if (off<0 || eoff>=(longT)img.size())
25320           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
25321                                       "Out-of-bounds image pointer "
25322                                       "(length: %ld, increment: %ld, offset start: %ld, "
25323                                       "offset end: %ld, offset max: %lu).",
25324                                       mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1);
25325         return (float*)&img[off];
25326       }
25327 
25328       static double mp_memcopy(_cimg_math_parser& mp) {
25329         longT siz = (longT)_mp_arg(4);
25330         const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6);
25331         const float
25332           _opacity = (float)_mp_arg(7),
25333           opacity = (float)cimg::abs(_opacity),
25334           omopacity = 1 - std::max(_opacity,0.f);
25335         if (siz>0) {
25336           const bool
25337             is_doubled = mp.opcode[8]<=1,
25338             is_doubles = mp.opcode[15]<=1;
25339           if (is_doubled && is_doubles) { // (double*) <- (double*)
25340             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
25341             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
25342             if (inc_d==1 && inc_s==1 && _opacity>=1) {
25343               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double));
25344               else std::memmove(ptrd,ptrs,siz*sizeof(double));
25345             } else {
25346               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
25347                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25348                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25349               } else { // Overlapping buffers
25350                 CImg<doubleT> buf((unsigned int)siz);
25351                 cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; }
25352                 ptrs = buf;
25353                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
25354                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
25355               }
25356             }
25357           } else if (is_doubled && !is_doubles) { // (double*) <- (float*)
25358             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
25359             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
25360             if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25361             else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25362           } else if (!is_doubled && is_doubles) { // (float*) <- (double*)
25363             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
25364             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
25365             if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25366             else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; }
25367           } else { // (float*) <- (float*)
25368             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
25369             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
25370             if (inc_d==1 && inc_s==1 && _opacity>=1) {
25371               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float));
25372               else std::memmove(ptrd,ptrs,siz*sizeof(float));
25373             } else {
25374               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
25375                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25376                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
25377               } else { // Overlapping buffers
25378                 CImg<floatT> buf((unsigned int)siz);
25379                 cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; }
25380                 ptrs = buf;
25381                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
25382                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
25383               }
25384             }
25385           }
25386         }
25387         return _mp_arg(1);
25388       }
25389 
25390       static double mp_min(_cimg_math_parser& mp) {
25391         const unsigned int i_end = (unsigned int)mp.opcode[2];
25392         double val = _mp_arg(3);
25393         for (unsigned int i = 4; i<i_end; ++i) val = std::min(val,_mp_arg(i));
25394         return val;
25395       }
25396 
25397       static double mp_minabs(_cimg_math_parser& mp) {
25398         const unsigned int i_end = (unsigned int)mp.opcode[2];
25399         double val = _mp_arg(3), absval = cimg::abs(val);
25400         for (unsigned int i = 4; i<i_end; ++i) {
25401           const double _val = _mp_arg(i), _absval = cimg::abs(_val);
25402           if (_absval<absval) { val = _val; absval = _absval; }
25403         }
25404         return val;
25405       }
25406 
25407       static double mp_minus(_cimg_math_parser& mp) {
25408         return -_mp_arg(2);
25409       }
25410 
25411       static double mp_median(_cimg_math_parser& mp) {
25412         const unsigned int i_end = (unsigned int)mp.opcode[2];
25413         switch (i_end - 3) {
25414         case 1 : return _mp_arg(3);
25415         case 2 : return cimg::median(_mp_arg(3),_mp_arg(4));
25416         case 3 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5));
25417         case 5 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7));
25418         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));
25419         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),
25420                                      _mp_arg(10),_mp_arg(11));
25421         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),
25422                                       _mp_arg(10),_mp_arg(11),_mp_arg(12),_mp_arg(13),_mp_arg(14),_mp_arg(15));
25423         }
25424         CImg<doubleT> vals(i_end - 3);
25425         double *p = vals.data();
25426         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
25427         return vals.median();
25428       }
25429 
25430       static double mp_modulo(_cimg_math_parser& mp) {
25431         return cimg::mod(_mp_arg(2),_mp_arg(3));
25432       }
25433 
25434       static double mp_mproj(_cimg_math_parser& mp) {
25435         double *ptrd = &_mp_arg(1) + 1;
25436         const double
25437           *ptrS = &_mp_arg(2) + 1,
25438           *ptrD = &_mp_arg(5) + 1;
25439         const unsigned int
25440           wS = (unsigned int)mp.opcode[3],
25441           hS = (unsigned int)mp.opcode[4],
25442           wD = (unsigned int)mp.opcode[6];
25443         const int
25444           method = std::max(0,(int)_mp_arg(7)),
25445           max_iter = std::max(0,(int)_mp_arg(8));
25446         const double
25447           max_residual = std::max(0.,_mp_arg(9));
25448 
25449         CImg<doubleT>(ptrd,wS,wD,1,1,true) = CImg<doubleT>(ptrS,wS,hS,1,1,false).
25450           project_matrix(CImg<doubleT>(ptrD,wD,hS,1,1,true),method,max_iter,max_residual);
25451         return cimg::type<double>::nan();
25452       }
25453 
25454       static double mp_mul(_cimg_math_parser& mp) {
25455         return _mp_arg(2)*_mp_arg(3);
25456       }
25457 
25458       static double mp_mul2(_cimg_math_parser& mp) {
25459         return _mp_arg(2)*_mp_arg(3)*_mp_arg(4);
25460       }
25461 
25462       static double mp_neq(_cimg_math_parser& mp) {
25463         return (double)(_mp_arg(2)!=_mp_arg(3));
25464       }
25465 
25466       static double mp_norm0(_cimg_math_parser& mp) {
25467         const unsigned int i_end = (unsigned int)mp.opcode[2];
25468         switch (i_end - 3) {
25469         case 1 : return _mp_arg(3)!=0;
25470         case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0);
25471         }
25472         double res = 0;
25473         for (unsigned int i = 3; i<i_end; ++i)
25474           res+=_mp_arg(i)==0?0:1;
25475         return res;
25476       }
25477 
25478       static double mp_norm1(_cimg_math_parser& mp) {
25479         const unsigned int i_end = (unsigned int)mp.opcode[2];
25480         switch (i_end - 3) {
25481         case 1 : return cimg::abs(_mp_arg(3));
25482         case 2 : return cimg::abs(_mp_arg(3)) + cimg::abs(_mp_arg(4));
25483         }
25484         double res = 0;
25485         for (unsigned int i = 3; i<i_end; ++i)
25486           res+=cimg::abs(_mp_arg(i));
25487         return res;
25488       }
25489 
25490       static double mp_norm2(_cimg_math_parser& mp) {
25491         const unsigned int i_end = (unsigned int)mp.opcode[2];
25492         switch (i_end - 3) {
25493         case 1 : return cimg::abs(_mp_arg(3));
25494         case 2 : return cimg::_hypot(_mp_arg(3),_mp_arg(4));
25495         }
25496         double res = 0;
25497         for (unsigned int i = 3; i<i_end; ++i)
25498           res+=cimg::sqr(_mp_arg(i));
25499         return std::sqrt(res);
25500       }
25501 
25502       static double mp_norminf(_cimg_math_parser& mp) {
25503         const unsigned int i_end = (unsigned int)mp.opcode[2];
25504         switch (i_end - 3) {
25505         case 1 : return cimg::abs(_mp_arg(3));
25506         case 2 : return std::max(cimg::abs(_mp_arg(3)),cimg::abs(_mp_arg(4)));
25507         }
25508         double res = 0;
25509         for (unsigned int i = 3; i<i_end; ++i) {
25510           const double val = cimg::abs(_mp_arg(i));
25511           if (val>res) res = val;
25512         }
25513         return res;
25514       }
25515 
25516       static double mp_normp(_cimg_math_parser& mp) {
25517         const unsigned int i_end = (unsigned int)mp.opcode[2];
25518         if (i_end==4) return cimg::abs(_mp_arg(3));
25519         const double p = (double)mp.opcode[3];
25520         double res = 0;
25521         for (unsigned int i = 4; i<i_end; ++i)
25522           res+=std::pow(cimg::abs(_mp_arg(i)),p);
25523         res = std::pow(res,1/p);
25524         return res>0?res:0.;
25525       }
25526 
25527       static double mp_permutations(_cimg_math_parser& mp) {
25528         return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4));
25529       }
25530 
25531       static double mp_polygon(_cimg_math_parser& mp) {
25532         const unsigned int i_end = (unsigned int)mp.opcode[2];
25533         unsigned int ind = (unsigned int)mp.opcode[3];
25534         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
25535         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
25536         bool is_invalid_arguments = i_end<=4, is_outlined = false;
25537         if (!is_invalid_arguments) {
25538           int nbv = (int)_mp_arg(4);
25539           if (!nbv) is_invalid_arguments = true;
25540           else {
25541             if (nbv<0) { nbv = -nbv; is_outlined = true; }
25542             CImg<intT> points(nbv,2,1,1,0);
25543             CImg<T> color(img._spectrum,1,1,1,0);
25544             float opacity = 1;
25545             unsigned int i = 5, pattern=~0U;
25546             cimg_foroff(points,k) if (i<i_end) points(k/2,k%2) = (int)cimg::round(_mp_arg(i++));
25547             else { is_invalid_arguments = true; break; }
25548             if (!is_invalid_arguments) {
25549               if (i<i_end) opacity = (float)_mp_arg(i++);
25550               if (is_outlined && i<i_end) pattern = (unsigned int)_mp_arg(i++);
25551               cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
25552               else { color.resize(k,1,1,1,-1); break; }
25553               color.resize(img._spectrum,1,1,1,0,2);
25554               if (is_outlined) img.draw_polygon(points,color._data,opacity,pattern);
25555               else img.draw_polygon(points,color._data,opacity);
25556             }
25557           }
25558         }
25559         if (is_invalid_arguments) {
25560           CImg<doubleT> args(i_end - 4);
25561           cimg_forX(args,k) args[k] = _mp_arg(4 + k);
25562           if (ind==~0U)
25563             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
25564                                         "Invalid arguments '%s'. ",
25565                                         mp.imgin.pixel_type(),args.value_string()._data);
25566           else
25567             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
25568                                         "Invalid arguments '#%u%s%s'. ",
25569                                         mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
25570         }
25571         return cimg::type<double>::nan();
25572       }
25573 
25574       static double mp_pow(_cimg_math_parser& mp) {
25575         const double v = _mp_arg(2), p = _mp_arg(3);
25576         return std::pow(v,p);
25577       }
25578 
25579       static double mp_pow0_25(_cimg_math_parser& mp) {
25580         const double val = _mp_arg(2);
25581         return std::sqrt(std::sqrt(val));
25582       }
25583 
25584       static double mp_pow3(_cimg_math_parser& mp) {
25585         const double val = _mp_arg(2);
25586         return val*val*val;
25587       }
25588 
25589       static double mp_pow4(_cimg_math_parser& mp) {
25590         const double val = _mp_arg(2);
25591         return val*val*val*val;
25592       }
25593 
25594       static double mp_print(_cimg_math_parser& mp) {
25595           const double val = _mp_arg(1);
25596           const bool print_char = (bool)mp.opcode[3];
25597           cimg_pragma_openmp(critical(mp_print))
25598           {
25599             CImg<charT> _expr(mp.opcode[2] - 4);
25600             const ulongT *ptrs = mp.opcode._data + 4;
25601             cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
25602             cimg::strellipsize(_expr);
25603             cimg::mutex(6);
25604             if (print_char)
25605               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g = '%c'",
25606                            _expr._data,val,(int)val);
25607             else
25608               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g",
25609                            _expr._data,val);
25610             std::fflush(cimg::output());
25611             cimg::mutex(6,0);
25612           }
25613           return val;
25614       }
25615 
25616       static double mp_prod(_cimg_math_parser& mp) {
25617         const unsigned int i_end = (unsigned int)mp.opcode[2];
25618         double val = _mp_arg(3);
25619         for (unsigned int i = 4; i<i_end; ++i) val*=_mp_arg(i);
25620         return val;
25621       }
25622 
25623       static double mp_copy(_cimg_math_parser& mp) {
25624         return _mp_arg(2);
25625       }
25626 
25627       static double mp_rol(_cimg_math_parser& mp) {
25628         return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3));
25629       }
25630 
25631       static double mp_ror(_cimg_math_parser& mp) {
25632         return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3));
25633       }
25634 
25635       static double mp_rot2d(_cimg_math_parser& mp) {
25636         double *ptrd = &_mp_arg(1) + 1;
25637         const float
25638           theta = (float)_mp_arg(2)*cimg::PI/180,
25639           ca = std::cos(theta),
25640           sa = std::sin(theta);
25641         *(ptrd++) = ca;
25642         *(ptrd++) = -sa;
25643         *(ptrd++) = sa;
25644         *ptrd = ca;
25645         return cimg::type<double>::nan();
25646       }
25647 
25648       static double mp_rot3d(_cimg_math_parser& mp) {
25649         double *ptrd = &_mp_arg(1) + 1;
25650         const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5);
25651         CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta);
25652         return cimg::type<double>::nan();
25653       }
25654 
25655       static double mp_round(_cimg_math_parser& mp) {
25656         return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
25657       }
25658 
25659       static double mp_self_add(_cimg_math_parser& mp) {
25660         return _mp_arg(1)+=_mp_arg(2);
25661       }
25662 
25663       static double mp_self_bitwise_and(_cimg_math_parser& mp) {
25664         double &val = _mp_arg(1);
25665         return val = (double)((longT)val & (longT)_mp_arg(2));
25666       }
25667 
25668       static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) {
25669         double &val = _mp_arg(1);
25670         return val = (double)((longT)val<<(unsigned int)_mp_arg(2));
25671       }
25672 
25673       static double mp_self_bitwise_or(_cimg_math_parser& mp) {
25674         double &val = _mp_arg(1);
25675         return val = (double)((longT)val | (longT)_mp_arg(2));
25676       }
25677 
25678       static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) {
25679         double &val = _mp_arg(1);
25680         return val = (double)((longT)val>>(unsigned int)_mp_arg(2));
25681       }
25682 
25683       static double mp_self_decrement(_cimg_math_parser& mp) {
25684         return --_mp_arg(1);
25685       }
25686 
25687       static double mp_self_increment(_cimg_math_parser& mp) {
25688         return ++_mp_arg(1);
25689       }
25690 
25691       static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar
25692         unsigned int
25693           ptrd = (unsigned int)mp.opcode[1] + 1,
25694           siz = (unsigned int)mp.opcode[2];
25695         mp_func op = (mp_func)mp.opcode[3];
25696         CImg<ulongT> l_opcode(1,3);
25697         l_opcode[2] = mp.opcode[4]; // Scalar argument
25698         l_opcode.swap(mp.opcode);
25699         ulongT &target = mp.opcode[1];
25700         while (siz-->0) { target = ptrd++; (*op)(mp); }
25701         l_opcode.swap(mp.opcode);
25702         return cimg::type<double>::nan();
25703       }
25704 
25705       static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector
25706         unsigned int
25707           ptrd = (unsigned int)mp.opcode[1] + 1,
25708           siz = (unsigned int)mp.opcode[2],
25709           ptrs = (unsigned int)mp.opcode[4] + 1;
25710         mp_func op = (mp_func)mp.opcode[3];
25711         CImg<ulongT> l_opcode(1,4);
25712         l_opcode.swap(mp.opcode);
25713         ulongT &target = mp.opcode[1], &argument = mp.opcode[2];
25714         while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); }
25715         l_opcode.swap(mp.opcode);
25716         return cimg::type<double>::nan();
25717       }
25718 
25719       static double mp_self_mul(_cimg_math_parser& mp) {
25720         return _mp_arg(1)*=_mp_arg(2);
25721       }
25722 
25723       static double mp_self_div(_cimg_math_parser& mp) {
25724         return _mp_arg(1)/=_mp_arg(2);
25725       }
25726 
25727       static double mp_self_modulo(_cimg_math_parser& mp) {
25728         double &val = _mp_arg(1);
25729         return val = cimg::mod(val,_mp_arg(2));
25730       }
25731 
25732       static double mp_self_pow(_cimg_math_parser& mp) {
25733         double &val = _mp_arg(1);
25734         return val = std::pow(val,_mp_arg(2));
25735       }
25736 
25737       static double mp_self_sub(_cimg_math_parser& mp) {
25738         return _mp_arg(1)-=_mp_arg(2);
25739       }
25740 
25741       static double mp_set_ioff(_cimg_math_parser& mp) {
25742         CImg<T> &img = mp.imgout;
25743         const longT
25744           off = (longT)_mp_arg(2),
25745           whds = (longT)img.size();
25746         const double val = _mp_arg(1);
25747         if (off>=0 && off<whds) img[off] = (T)val;
25748         return val;
25749       }
25750 
25751       static double mp_set_ixyzc(_cimg_math_parser& mp) {
25752         CImg<T> &img = mp.imgout;
25753         const int
25754           x = (int)_mp_arg(2), y = (int)_mp_arg(3),
25755           z = (int)_mp_arg(4), c = (int)_mp_arg(5);
25756         const double val = _mp_arg(1);
25757         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
25758             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
25759           img(x,y,z,c) = (T)val;
25760         return val;
25761       }
25762 
25763       static double mp_set_joff(_cimg_math_parser& mp) {
25764         CImg<T> &img = mp.imgout;
25765         const int
25766           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25767           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25768         const longT
25769           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
25770           whds = (longT)img.size();
25771         const double val = _mp_arg(1);
25772         if (off>=0 && off<whds) img[off] = (T)val;
25773         return val;
25774       }
25775 
25776       static double mp_set_jxyzc(_cimg_math_parser& mp) {
25777         CImg<T> &img = mp.imgout;
25778         const double
25779           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
25780           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
25781         const int
25782           x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)),
25783           z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5));
25784         const double val = _mp_arg(1);
25785         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
25786             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
25787           img(x,y,z,c) = (T)val;
25788         return val;
25789       }
25790 
25791       static double mp_set_Ioff_s(_cimg_math_parser& mp) {
25792         CImg<T> &img = mp.imgout;
25793         const longT
25794           off = (longT)_mp_arg(2),
25795           whd = (longT)img.width()*img.height()*img.depth();
25796         const T val = (T)_mp_arg(1);
25797         if (off>=0 && off<whd) {
25798           T *ptrd = &img[off];
25799           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25800         }
25801         return _mp_arg(1);
25802       }
25803 
25804       static double mp_set_Ioff_v(_cimg_math_parser& mp) {
25805         CImg<T> &img = mp.imgout;
25806         const longT
25807           off = (longT)_mp_arg(2),
25808           whd = (longT)img.width()*img.height()*img.depth();
25809         const double *ptrs = &_mp_arg(1) + 1;
25810         if (off>=0 && off<whd) {
25811           const unsigned int vsiz = (unsigned int)mp.opcode[3];
25812           T *ptrd = &img[off];
25813           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25814         }
25815         return cimg::type<double>::nan();
25816       }
25817 
25818       static double mp_set_Ixyz_s(_cimg_math_parser& mp) {
25819         CImg<T> &img = mp.imgout;
25820         const int
25821           x = (int)_mp_arg(2),
25822           y = (int)_mp_arg(3),
25823           z = (int)_mp_arg(4);
25824         const T val = (T)_mp_arg(1);
25825         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25826           T *ptrd = &img(x,y,z);
25827           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25828           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25829         }
25830         return _mp_arg(1);
25831       }
25832 
25833       static double mp_set_Ixyz_v(_cimg_math_parser& mp) {
25834         CImg<T> &img = mp.imgout;
25835         const int
25836           x = (int)_mp_arg(2),
25837           y = (int)_mp_arg(3),
25838           z = (int)_mp_arg(4);
25839         const double *ptrs = &_mp_arg(1) + 1;
25840         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25841           const unsigned int vsiz = (unsigned int)mp.opcode[5];
25842           T *ptrd = &img(x,y,z);
25843           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25844           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25845         }
25846         return cimg::type<double>::nan();
25847       }
25848 
25849       static double mp_set_Joff_s(_cimg_math_parser& mp) {
25850         CImg<T> &img = mp.imgout;
25851         const int
25852           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25853           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25854         const longT
25855           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
25856           whd = (longT)img.width()*img.height()*img.depth();
25857         const T val = (T)_mp_arg(1);
25858         if (off>=0 && off<whd) {
25859           T *ptrd = &img[off];
25860           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25861         }
25862         return _mp_arg(1);
25863       }
25864 
25865       static double mp_set_Joff_v(_cimg_math_parser& mp) {
25866         CImg<T> &img = mp.imgout;
25867         const int
25868           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
25869           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
25870         const longT
25871           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
25872           whd = (longT)img.width()*img.height()*img.depth();
25873         const double *ptrs = &_mp_arg(1) + 1;
25874         if (off>=0 && off<whd) {
25875           const unsigned int vsiz = (unsigned int)mp.opcode[3];
25876           T *ptrd = &img[off];
25877           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25878         }
25879         return cimg::type<double>::nan();
25880       }
25881 
25882       static double mp_set_Jxyz_s(_cimg_math_parser& mp) {
25883         CImg<T> &img = mp.imgout;
25884         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
25885         const int
25886           x = (int)(ox + _mp_arg(2)),
25887           y = (int)(oy + _mp_arg(3)),
25888           z = (int)(oz + _mp_arg(4));
25889         const T val = (T)_mp_arg(1);
25890         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25891           T *ptrd = &img(x,y,z);
25892           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25893           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
25894         }
25895         return _mp_arg(1);
25896       }
25897 
25898       static double mp_set_Jxyz_v(_cimg_math_parser& mp) {
25899         CImg<T> &img = mp.imgout;
25900         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
25901         const int
25902           x = (int)(ox + _mp_arg(2)),
25903           y = (int)(oy + _mp_arg(3)),
25904           z = (int)(oz + _mp_arg(4));
25905         const double *ptrs = &_mp_arg(1) + 1;
25906         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
25907           const unsigned int vsiz = (unsigned int)mp.opcode[5];
25908           T *ptrd = &img(x,y,z);
25909           const ulongT whd = (ulongT)img._width*img._height*img._depth;
25910           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
25911         }
25912         return cimg::type<double>::nan();
25913       }
25914 
25915       static double mp_shift(_cimg_math_parser& mp) {
25916         double *const ptrd = &_mp_arg(1) + 1;
25917         const double *const ptrs = &_mp_arg(2) + 1;
25918         const unsigned int siz = (unsigned int)mp.opcode[3];
25919         const int
25920           shift = (int)_mp_arg(4),
25921           boundary_conditions = (int)_mp_arg(5);
25922         CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions);
25923         return cimg::type<double>::nan();
25924       }
25925 
25926       static double mp_sign(_cimg_math_parser& mp) {
25927         return cimg::sign(_mp_arg(2));
25928       }
25929 
25930       static double mp_sin(_cimg_math_parser& mp) {
25931         return std::sin(_mp_arg(2));
25932       }
25933 
25934       static double mp_sinc(_cimg_math_parser& mp) {
25935         return cimg::sinc(_mp_arg(2));
25936       }
25937 
25938       static double mp_sinh(_cimg_math_parser& mp) {
25939         return std::sinh(_mp_arg(2));
25940       }
25941 
25942       static double mp_solve(_cimg_math_parser& mp) {
25943         double *ptrd = &_mp_arg(1) + 1;
25944         const double
25945           *ptr1 = &_mp_arg(2) + 1,
25946           *ptr2 = &_mp_arg(3) + 1;
25947         const unsigned int
25948           k = (unsigned int)mp.opcode[4],
25949           l = (unsigned int)mp.opcode[5],
25950           m = (unsigned int)mp.opcode[6];
25951         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));
25952         return cimg::type<double>::nan();
25953       }
25954 
25955       static double mp_sort(_cimg_math_parser& mp) {
25956         double *const ptrd = &_mp_arg(1) + 1;
25957         const double *const ptrs = &_mp_arg(2) + 1;
25958         const bool is_increasing = (bool)_mp_arg(4);
25959         const unsigned int
25960           siz = (unsigned int)mp.opcode[3],
25961           nb_elts = mp.opcode[5]==~0U?siz:(unsigned int)_mp_arg(5),
25962           siz_elt = (unsigned int)_mp_arg(6);
25963         const ulongT sn = siz_elt*nb_elts;
25964         if (sn>siz || siz_elt<1)
25965           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'sort()': "
25966                                       "Arguments 'nb_elts=%g' and 'siz_elt=%g' are invalid "
25967                                       "for sorting a vector of size %u.",
25968                                       mp.imgin.pixel_type(),_mp_arg(5),_mp_arg(6),siz);
25969         CImg<doubleT>(ptrd,siz_elt,nb_elts,1,1,true) = CImg<doubleT>(ptrs,siz_elt,nb_elts,1,1,true).
25970           get_sort(is_increasing,siz_elt>1?'y':0);
25971         if (sn<siz) CImg<doubleT>(ptrd + sn,siz - sn,1,1,1,true) = CImg<doubleT>(ptrs + sn,siz - sn,1,1,1,true);
25972         return cimg::type<double>::nan();
25973       }
25974 
25975       static double mp_sqr(_cimg_math_parser& mp) {
25976         return cimg::sqr(_mp_arg(2));
25977       }
25978 
25979       static double mp_sqrt(_cimg_math_parser& mp) {
25980         return std::sqrt(_mp_arg(2));
25981       }
25982 
25983       static double mp_srand(_cimg_math_parser& mp) {
25984         mp.rng = (cimg_uint64)_mp_arg(2);
25985         return cimg::type<double>::nan();
25986       }
25987 
25988       static double mp_srand0(_cimg_math_parser& mp) {
25989         cimg::srand(&mp.rng);
25990 
25991 #if cimg_use_openmp!=0
25992         mp.rng+=omp_get_thread_num();
25993 #endif
25994         return cimg::type<double>::nan();
25995       }
25996 
25997       static double mp_std(_cimg_math_parser& mp) {
25998         const unsigned int i_end = (unsigned int)mp.opcode[2];
25999         CImg<doubleT> vals(i_end - 3);
26000         double *p = vals.data();
26001         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
26002         return std::sqrt(vals.variance());
26003       }
26004 
26005       static double mp_string_init(_cimg_math_parser& mp) {
26006         const unsigned char *ptrs = (unsigned char*)&mp.opcode[3];
26007         unsigned int
26008           ptrd = (unsigned int)mp.opcode[1] + 1,
26009           siz = (unsigned int)mp.opcode[2];
26010         while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++);
26011         return cimg::type<double>::nan();
26012       }
26013 
26014 #ifdef cimg_mp_func_store
26015       static double mp_store(_cimg_math_parser& mp) {
26016         const double
26017           *ptr1 = &_mp_arg(2),
26018           *ptr2 = &_mp_arg(4) + 1;
26019         const unsigned int
26020           siz1 = (unsigned int)mp.opcode[3],
26021           siz2 = (unsigned int)mp.opcode[5],
26022           sizM = std::max(siz1,1U);
26023         const int
26024           w = (int)_mp_arg(6),
26025           h = (int)_mp_arg(7),
26026           d = (int)_mp_arg(8),
26027           s = (int)_mp_arg(9);
26028 
26029         const bool is_compressed = (bool)_mp_arg(10);
26030         if (w<0 || h<0 || d<0 || s<0)
26031           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': "
26032                                       "Specified image dimensions (%d,%d,%d,%d) are invalid.",
26033                                       pixel_type(),w,h,d,s);
26034         if ((unsigned int)w*h*d*s>sizM)
26035           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': "
26036                                       "Specified image dimensions (%d,%d,%d,%d) are too large for vector size (%u).",
26037                                       pixel_type(),w,h,d,s,sizM);
26038         CImg<charT> ss(siz2 + 1);
26039         cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i];
26040         ss.back() = 0;
26041         if (siz1) cimg_mp_func_store(ptr1 + 1,
26042                                      (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
26043                                      is_compressed,ss._data);
26044         else cimg_mp_func_store(ptr1,1,1,1,1,is_compressed,ss._data);
26045         return cimg::type<double>::nan();
26046       }
26047 #endif
26048 
26049       static double mp_stov(_cimg_math_parser& mp) {
26050         const double *ptrs = &_mp_arg(2);
26051         const ulongT siz = (ulongT)mp.opcode[3];
26052         longT ind = (longT)_mp_arg(4);
26053         const bool is_strict = (bool)_mp_arg(5);
26054         double val = cimg::type<double>::nan();
26055         if (ind<0 || ind>=(longT)siz) return val;
26056         if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val;
26057 
26058         CImg<charT> ss(siz + 1 - ind);
26059         ptrs+=1 + ind;
26060         cimg_forX(ss,i) ss[i] = (char)ptrs[i];
26061         ss.back() = 0;
26062 
26063         const char *s = ss._data;
26064         while (*s && *s<=32) ++s;
26065         const bool is_negative = *s=='-';
26066         if (is_negative || *s=='+') ++s;
26067         int err = 0;
26068         char sep;
26069 
26070         if (*s=='0' && (s[1]=='x' || s[1]=='X') && s[2]>32) { // Hexadecimal number
26071           unsigned int ival;
26072           err = cimg_sscanf(s + 2,"%x%c",&ival,&sep);
26073           if (err>0) val = (double)ival;
26074         } else if (*s>32) { // Decimal number
26075           err = cimg_sscanf(s,"%lf%c",&val,&sep);
26076 #if cimg_OS==2
26077           // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
26078           // to read those particular values.
26079           if (!err && (*s=='i' || *s=='I' || *s=='n' || *s=='N')) {
26080             if (!cimg::strncasecmp(s,"inf",3)) { val = cimg::type<double>::inf(); err = 1 + (s[3]!=0); }
26081             else if (!cimg::strncasecmp(s,"nan",3)) { val = cimg::type<double>::nan(); err = 1 + (s[3]!=0); }
26082           }
26083 #endif
26084         }
26085         if (err<=0 || (is_strict && err!=1)) return cimg::type<double>::nan();
26086         if (is_negative) val = -val;
26087         return val;
26088       }
26089 
26090       static double mp_string(_cimg_math_parser& mp) {
26091         double *const ptrd = &_mp_arg(1) + 1;
26092         const unsigned int nb_args = (unsigned int)(mp.opcode[3] - 3)/2;
26093         CImgList<charT> _str;
26094         CImg<charT> it;
26095         for (unsigned int n = 0; n<nb_args; ++n) {
26096           const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n];
26097           if (siz) { // Vector argument -> string
26098             const double *ptr = &_mp_arg(4 + 2*n) + 1;
26099             unsigned int l = 0;
26100             while (l<siz && ptr[l]) ++l;
26101             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
26102           } else { // Scalar argument -> number
26103             it.assign(24);
26104             cimg_snprintf(it,it._width,"%.17g",_mp_arg(4 + 2*n));
26105             CImg<charT>::string(it,false,true).move_to(_str);
26106           }
26107         }
26108         const CImg<charT> str = _str>'x';
26109         const unsigned int sizd = std::min(str._width,(unsigned int)mp.opcode[2]);
26110         std::memset(ptrd,0,mp.opcode[2]*sizeof(double));
26111         for (unsigned int k = 0; k<sizd; ++k) ptrd[k] = (double)str[k];
26112         return cimg::type<double>::nan();
26113       }
26114 
26115       static double mp_sub(_cimg_math_parser& mp) {
26116         return _mp_arg(2) - _mp_arg(3);
26117       }
26118 
26119       static double mp_sum(_cimg_math_parser& mp) {
26120         const unsigned int i_end = (unsigned int)mp.opcode[2];
26121         double val = _mp_arg(3);
26122         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
26123         return val;
26124       }
26125 
26126       static double mp_swap(_cimg_math_parser& mp) {
26127         const unsigned int siz = (unsigned int)mp.opcode[3];
26128         if (!siz) { // Scalar
26129           double &arg1 = _mp_arg(1), &arg2 = _mp_arg(2);
26130           cimg::swap(arg1,arg2);
26131         } else { // Vector
26132           double *const ptr1 = &_mp_arg(1) + 1, *const ptr2 = &_mp_arg(2) + 1;
26133           for (unsigned int k = 0; k<siz; ++k) cimg::swap(ptr1[k],ptr2[k]);
26134         }
26135         return _mp_arg(1);
26136       }
26137 
26138       static double mp_tan(_cimg_math_parser& mp) {
26139         return std::tan(_mp_arg(2));
26140       }
26141 
26142       static double mp_tanh(_cimg_math_parser& mp) {
26143         return std::tanh(_mp_arg(2));
26144       }
26145 
26146       static double mp_trace(_cimg_math_parser& mp) {
26147         const double *ptrs = &_mp_arg(2) + 1;
26148         const unsigned int k = (unsigned int)mp.opcode[3];
26149         return CImg<doubleT>(ptrs,k,k,1,1,true).trace();
26150       }
26151 
26152       static double mp_transpose(_cimg_math_parser& mp) {
26153         double *ptrd = &_mp_arg(1) + 1;
26154         const double *ptrs = &_mp_arg(2) + 1;
26155         const unsigned int
26156           k = (unsigned int)mp.opcode[3],
26157           l = (unsigned int)mp.opcode[4];
26158         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose();
26159         return cimg::type<double>::nan();
26160       }
26161 
26162       static double mp_u(_cimg_math_parser& mp) {
26163         return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng);
26164       }
26165 
26166       static double mp_ui2f(_cimg_math_parser& mp) {
26167         return (double)cimg::uint2float((unsigned int)_mp_arg(2));
26168       }
26169 
26170       static double mp_uppercase(_cimg_math_parser& mp) {
26171         return cimg::uppercase(_mp_arg(2));
26172       }
26173 
26174       static double mp_var(_cimg_math_parser& mp) {
26175         const unsigned int i_end = (unsigned int)mp.opcode[2];
26176         CImg<doubleT> vals(i_end - 3);
26177         double *p = vals.data();
26178         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
26179         return vals.variance();
26180       }
26181 
26182       static double mp_vector_copy(_cimg_math_parser& mp) {
26183         std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]);
26184         return cimg::type<double>::nan();
26185       }
26186 
26187       static double mp_vector_crop(_cimg_math_parser& mp) {
26188         double *ptrd = &_mp_arg(1) + 1;
26189         const double *ptrs = &_mp_arg(2) + 1;
26190         const longT
26191           length = (longT)mp.opcode[3],
26192           start = (longT)_mp_arg(4),
26193           sublength = (longT)mp.opcode[5],
26194           step = (longT)_mp_arg(6);
26195         if (start<0 || start + step*(sublength-1)>=length)
26196           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': "
26197                                       "Out-of-bounds sub-vector request "
26198                                       "(length: %ld, start: %ld, sub-length: %ld, step: %ld).",
26199                                       mp.imgin.pixel_type(),length,start,sublength,step);
26200         ptrs+=start;
26201         if (step==1) std::memcpy(ptrd,ptrs,sublength*sizeof(double));
26202         else for (longT k = 0; k<sublength; ++k) { *(ptrd++) = *ptrs; ptrs+=step; }
26203         return cimg::type<double>::nan();
26204       }
26205 
26206       static double mp_vector_init(_cimg_math_parser& mp) {
26207         unsigned int
26208           ptrs = 4U,
26209           ptrd = (unsigned int)mp.opcode[1] + 1,
26210           siz = (unsigned int)mp.opcode[3];
26211         switch (mp.opcode[2] - 4) {
26212         case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given
26213         case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break;
26214         default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; }
26215         }
26216         return cimg::type<double>::nan();
26217       }
26218 
26219       static double mp_vector_eq(_cimg_math_parser& mp) {
26220         const double
26221           *ptr1 = &_mp_arg(2) + 1,
26222           *ptr2 = &_mp_arg(4) + 1;
26223         unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n;
26224         const int N = (int)_mp_arg(6);
26225         const bool case_sensitive = (bool)_mp_arg(7);
26226         bool still_equal = true;
26227         double value;
26228         if (!N) return true;
26229 
26230         // Compare all values.
26231         if (N<0) {
26232           if (p1>0 && p2>0) { // Vector == vector
26233             if (p1!=p2) return false;
26234             if (case_sensitive)
26235               while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++);
26236             else
26237               while (still_equal && p1--)
26238                 still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
26239             return still_equal;
26240           } else if (p1>0 && !p2) { // Vector == scalar
26241             value = _mp_arg(4);
26242             if (!case_sensitive) value = cimg::lowercase(value);
26243             while (still_equal && p1--) still_equal = *(ptr1++)==value;
26244             return still_equal;
26245           } else if (!p1 && p2>0) { // Scalar == vector
26246             value = _mp_arg(2);
26247             if (!case_sensitive) value = cimg::lowercase(value);
26248             while (still_equal && p2--) still_equal = *(ptr2++)==value;
26249             return still_equal;
26250           } else { // Scalar == scalar
26251             if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
26252             else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
26253           }
26254         }
26255 
26256         // Compare only first N values.
26257         if (p1>0 && p2>0) { // Vector == vector
26258           n = cimg::min((unsigned int)N,p1,p2);
26259           if (case_sensitive)
26260             while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++);
26261           else
26262             while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
26263           return still_equal;
26264         } else if (p1>0 && !p2) { // Vector == scalar
26265           n = std::min((unsigned int)N,p1);
26266           value = _mp_arg(4);
26267           if (!case_sensitive) value = cimg::lowercase(value);
26268           while (still_equal && n--) still_equal = *(ptr1++)==value;
26269           return still_equal;
26270         } else if (!p1 && p2>0) { // Scalar == vector
26271           n = std::min((unsigned int)N,p2);
26272           value = _mp_arg(2);
26273           if (!case_sensitive) value = cimg::lowercase(value);
26274           while (still_equal && n--) still_equal = *(ptr2++)==value;
26275           return still_equal;
26276         }  // Scalar == scalar
26277         if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
26278         return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
26279       }
26280 
26281       static double mp_vector_lerp(_cimg_math_parser& mp) {
26282         unsigned int siz = (unsigned int)mp.opcode[2];
26283         double *ptrd = &_mp_arg(1) + 1;
26284         const double
26285           *ptrs1 = &_mp_arg(3) + 1,
26286           *ptrs2 = &_mp_arg(4) + 1,
26287           t = _mp_arg(5);
26288         for (unsigned int k = 0; k<siz; ++k) ptrd[k] = ptrs1[k]*(1-t) + ptrs2[k]*t;
26289         return cimg::type<double>::nan();
26290       }
26291 
26292       static double mp_vector_off(_cimg_math_parser& mp) {
26293         const unsigned int
26294           ptr = (unsigned int)mp.opcode[2] + 1,
26295           siz = (unsigned int)mp.opcode[3];
26296         const int off = (int)_mp_arg(4);
26297         return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();
26298       }
26299 
26300       static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector)
26301         unsigned int
26302           siz = (unsigned int)mp.opcode[2],
26303           ptrs = (unsigned int)mp.opcode[5] + 1;
26304         double *ptrd = &_mp_arg(1) + 1;
26305         mp_func op = (mp_func)mp.opcode[3];
26306         CImg<ulongT> l_opcode(4);
26307         l_opcode[2] = mp.opcode[4]; // Scalar argument1
26308         l_opcode.swap(mp.opcode);
26309         ulongT &argument2 = mp.opcode[3];
26310         while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); }
26311         l_opcode.swap(mp.opcode);
26312         return cimg::type<double>::nan();
26313       }
26314 
26315       static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector)
26316         unsigned int
26317           siz = (unsigned int)mp.opcode[2],
26318           ptrs = (unsigned int)mp.opcode[4] + 1;
26319         double *ptrd = &_mp_arg(1) + 1;
26320         mp_func op = (mp_func)mp.opcode[3];
26321         CImg<ulongT> l_opcode(1,3);
26322         l_opcode.swap(mp.opcode);
26323         ulongT &argument = mp.opcode[2];
26324         while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); }
26325         l_opcode.swap(mp.opcode);
26326         return cimg::type<double>::nan();
26327       }
26328 
26329       static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar)
26330         unsigned int
26331           siz = (unsigned int)mp.opcode[2],
26332           ptrs = (unsigned int)mp.opcode[4] + 1;
26333         double *ptrd = &_mp_arg(1) + 1;
26334         mp_func op = (mp_func)mp.opcode[3];
26335         CImg<ulongT> l_opcode(1,4);
26336         l_opcode[3] = mp.opcode[5]; // Scalar argument2
26337         l_opcode.swap(mp.opcode);
26338         ulongT &argument1 = mp.opcode[2];
26339         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
26340         l_opcode.swap(mp.opcode);
26341         return cimg::type<double>::nan();
26342       }
26343 
26344       static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar)
26345         unsigned int
26346           siz = (unsigned int)mp.opcode[2],
26347           ptrs = (unsigned int)mp.opcode[4] + 1;
26348         double *ptrd = &_mp_arg(1) + 1;
26349         mp_func op = (mp_func)mp.opcode[3];
26350         CImg<ulongT> l_opcode(1,5);
26351         l_opcode[3] = mp.opcode[5]; // Scalar argument2
26352         l_opcode[4] = mp.opcode[6]; // Scalar argument3
26353         l_opcode.swap(mp.opcode);
26354         ulongT &argument1 = mp.opcode[2];
26355         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
26356         l_opcode.swap(mp.opcode);
26357         return cimg::type<double>::nan();
26358       }
26359 
26360       static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector)
26361         unsigned int
26362           siz = (unsigned int)mp.opcode[2],
26363           ptrs1 = (unsigned int)mp.opcode[4] + 1,
26364           ptrs2 = (unsigned int)mp.opcode[5] + 1;
26365         double *ptrd = &_mp_arg(1) + 1;
26366         mp_func op = (mp_func)mp.opcode[3];
26367         CImg<ulongT> l_opcode(1,4);
26368         l_opcode.swap(mp.opcode);
26369         ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3];
26370         while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); }
26371         l_opcode.swap(mp.opcode);
26372         return cimg::type<double>::nan();
26373       }
26374 
26375       static double mp_vector_neq(_cimg_math_parser& mp) {
26376         return !mp_vector_eq(mp);
26377       }
26378 
26379       static double mp_vector_print(_cimg_math_parser& mp) {
26380         const bool print_string = (bool)mp.opcode[4];
26381         cimg_pragma_openmp(critical(mp_vector_print))
26382         {
26383           CImg<charT> _expr(mp.opcode[2] - 5);
26384           const ulongT *ptrs = mp.opcode._data + 5;
26385           cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
26386           cimg::strellipsize(_expr);
26387           unsigned int
26388             ptr = (unsigned int)mp.opcode[1] + 1,
26389             siz0 = (unsigned int)mp.opcode[3],
26390             siz = siz0;
26391           cimg::mutex(6);
26392           std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",_expr._data);
26393           unsigned int count = 0;
26394           while (siz-->0) {
26395             if (count>=64 && siz>=64) {
26396               std::fprintf(cimg::output(),"...,");
26397               ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64;
26398               siz = 64;
26399             } else std::fprintf(cimg::output(),"%.17g%s",mp.mem[ptr++],siz?",":"");
26400             ++count;
26401           }
26402           if (print_string) {
26403             CImg<charT> str(siz0 + 1);
26404             ptr = (unsigned int)mp.opcode[1] + 1;
26405             for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++];
26406             str[siz0] = 0;
26407             cimg::strellipsize(str,1024,false);
26408             std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0);
26409           } else std::fprintf(cimg::output()," ] (size: %u)",siz0);
26410           std::fflush(cimg::output());
26411           cimg::mutex(6,0);
26412         }
26413         return cimg::type<double>::nan();
26414       }
26415 
26416       static double mp_vector_resize(_cimg_math_parser& mp) {
26417         double *const ptrd = &_mp_arg(1) + 1;
26418         const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
26419         const int
26420           interpolation = (int)_mp_arg(5),
26421           boundary_conditions = (int)_mp_arg(6);
26422         if (p2) { // Resize vector
26423           const double *const ptrs = &_mp_arg(3) + 1;
26424           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
26425             get_resize(p1,1,1,1,interpolation,boundary_conditions);
26426         } else { // Resize scalar
26427           const double value = _mp_arg(3);
26428           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
26429                                                                                   boundary_conditions);
26430         }
26431         return cimg::type<double>::nan();
26432       }
26433 
26434       static double mp_vector_reverse(_cimg_math_parser& mp) {
26435         double *const ptrd = &_mp_arg(1) + 1;
26436         const double *const ptrs = &_mp_arg(2) + 1;
26437         const unsigned int p1 = (unsigned int)mp.opcode[3];
26438         CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
26439         return cimg::type<double>::nan();
26440       }
26441 
26442       static double mp_vector_set_off(_cimg_math_parser& mp) {
26443         const unsigned int
26444           ptr = (unsigned int)mp.opcode[2] + 1,
26445           siz = (unsigned int)mp.opcode[3];
26446         const int off = (int)_mp_arg(4);
26447         if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(1);
26448         return _mp_arg(1);
26449       }
26450 
26451 #define _cimg_mp_vfunc(func) \
26452       const longT sizd = (longT)mp.opcode[2];\
26453       const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \
26454       double *const ptrd = &_mp_arg(1) + (sizd?1:0); \
26455       cimg_pragma_openmp(parallel cimg_openmp_if_size(sizd,256)) \
26456       { CImg<doubleT> vec(nbargs); double res; \
26457         cimg_pragma_openmp(for) for (longT k = sizd?sizd - 1:0; k>=0; --k) { \
26458           cimg_forX(vec,n) vec[n] = *(&_mp_arg(4 + 2*n) + (k+1)*(mp.opcode[4 + 2*n + 1]?1:0)); \
26459           func; ptrd[k] = res; \
26460       }} \
26461       return sizd?cimg::type<double>::nan():*ptrd;
26462 
26463       static double _mp_vargkth(CImg<doubleT>& vec) {
26464         const double val = (+vec).get_shared_points(1,vec.width() - 1).
26465           kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2));
26466         cimg_for_inX(vec,1,vec.width()-1,ind) if (vec[ind]==val) return ind - 1.;
26467         return 1.;
26468       }
26469 
26470       static double mp_vargkth(_cimg_math_parser& mp) {
26471         _cimg_mp_vfunc(res = _mp_vargkth(vec));
26472       }
26473 
26474       static double mp_vargmax(_cimg_math_parser& mp) {
26475         _cimg_mp_vfunc(res = (double)(&vec.max() - vec.data()));
26476       }
26477 
26478       static double mp_vargmaxabs(_cimg_math_parser& mp) {
26479         _cimg_mp_vfunc(res = (double)(&vec.maxabs() - vec.data()));
26480       }
26481 
26482       static double mp_vargmin(_cimg_math_parser& mp) {
26483         _cimg_mp_vfunc(res = (double)(&vec.min() - vec.data()));
26484       }
26485 
26486       static double mp_vargminabs(_cimg_math_parser& mp) {
26487         _cimg_mp_vfunc(res = (double)(&vec.minabs() - vec.data()));
26488       }
26489 
26490       static double mp_vavg(_cimg_math_parser& mp) {
26491         _cimg_mp_vfunc(res = vec.mean());
26492       }
26493 
26494       static double mp_vkth(_cimg_math_parser& mp) {
26495         _cimg_mp_vfunc(res = vec.get_shared_points(1,vec.width() - 1).
26496                        kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2)));
26497       }
26498 
26499       static double mp_vmax(_cimg_math_parser& mp) {
26500         _cimg_mp_vfunc(res = vec.max());
26501       }
26502 
26503       static double mp_vmaxabs(_cimg_math_parser& mp) {
26504         _cimg_mp_vfunc(res = vec.maxabs());
26505       }
26506 
26507       static double mp_vmedian(_cimg_math_parser& mp) {
26508         _cimg_mp_vfunc(res = vec.median());
26509       }
26510 
26511       static double mp_vmin(_cimg_math_parser& mp) {
26512         _cimg_mp_vfunc(res = vec.min());
26513       }
26514 
26515       static double mp_vminabs(_cimg_math_parser& mp) {
26516         _cimg_mp_vfunc(res = vec.minabs());
26517       }
26518 
26519       static double mp_vprod(_cimg_math_parser& mp) {
26520         _cimg_mp_vfunc(res = vec.product());
26521       }
26522 
26523       static double mp_vstd(_cimg_math_parser& mp) {
26524         _cimg_mp_vfunc(res = std::sqrt(vec.get_stats()[3]));
26525       }
26526 
26527       static double mp_vsum(_cimg_math_parser& mp) {
26528         _cimg_mp_vfunc(res = vec.sum());
26529       }
26530 
26531       static double mp_vvar(_cimg_math_parser& mp) {
26532         _cimg_mp_vfunc(res = vec.get_stats()[3]);
26533       }
26534 
26535       static double mp_vtos(_cimg_math_parser& mp) {
26536         double *ptrd = &_mp_arg(1) + 1;
26537         const unsigned int
26538           sizd = (unsigned int)mp.opcode[2],
26539           sizs = (unsigned int)mp.opcode[4];
26540         std::memset(ptrd,0,sizd*sizeof(double));
26541         const int nb_digits = (int)_mp_arg(5);
26542         CImg<charT> format(8);
26543         switch (nb_digits) {
26544         case -1 : std::strcpy(format,"%g"); break;
26545         case 0 : std::strcpy(format,"%.17g"); break;
26546         default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
26547         }
26548         CImg<charT> str;
26549         if (sizs) { // Vector expression
26550           const double *ptrs = &_mp_arg(3) + 1;
26551           CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
26552         } else { // Scalar expression
26553           str.assign(sizd + 1);
26554           cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
26555         }
26556         const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
26557         CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
26558         return cimg::type<double>::nan();
26559      }
26560 
26561       static double mp_while(_cimg_math_parser& mp) {
26562         const ulongT
26563           mem_body = mp.opcode[1],
26564           mem_cond = mp.opcode[2];
26565         const CImg<ulongT>
26566           *const p_cond = ++mp.p_code,
26567           *const p_body = p_cond + mp.opcode[3],
26568           *const p_end = p_body + mp.opcode[4];
26569         const unsigned int vsiz = (unsigned int)mp.opcode[5];
26570         bool is_cond = false;
26571         if (mp.opcode[6]) { // Set default value for result and condition if necessary
26572           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
26573           else mp.mem[mem_body] = cimg::type<double>::nan();
26574         }
26575         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
26576         const unsigned int _break_type = mp.break_type;
26577         mp.break_type = 0;
26578         do {
26579           for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
26580             mp.opcode._data = mp.p_code->_data;
26581             const ulongT target = mp.opcode[1];
26582             mp.mem[target] = _cimg_mp_defunc(mp);
26583           }
26584           if (mp.break_type==1) break;
26585           is_cond = (bool)mp.mem[mem_cond];
26586           if (is_cond && !mp.break_type) // Evaluate body
26587             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
26588               mp.opcode._data = mp.p_code->_data;
26589               const ulongT target = mp.opcode[1];
26590               mp.mem[target] = _cimg_mp_defunc(mp);
26591             }
26592           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
26593         } while (is_cond);
26594 
26595         mp.break_type = _break_type;
26596         mp.p_code = p_end - 1;
26597         return mp.mem[mem_body];
26598       }
26599 
26600       static double mp_Ioff(_cimg_math_parser& mp) {
26601         double *ptrd = &_mp_arg(1) + 1;
26602         const unsigned int
26603           boundary_conditions = (unsigned int)_mp_arg(3),
26604           vsiz = (unsigned int)mp.opcode[4];
26605         const CImg<T> &img = mp.imgin;
26606         const longT
26607           off = (longT)_mp_arg(2),
26608           whd = (longT)img.width()*img.height()*img.depth();
26609         const T *ptrs;
26610         if (off>=0 && off<whd) {
26611           ptrs = &img[off];
26612           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26613           return cimg::type<double>::nan();
26614         }
26615         if (img._data) switch (boundary_conditions) {
26616           case 3 : { // Mirror
26617             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
26618             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
26619             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26620             return cimg::type<double>::nan();
26621           }
26622           case 2 : // Periodic
26623             ptrs = &img[cimg::mod(off,whd)];
26624             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26625             return cimg::type<double>::nan();
26626           case 1 : // Neumann
26627             ptrs = off<0?&img[0]:&img[whd - 1];
26628             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26629             return cimg::type<double>::nan();
26630           default : // Dirichlet
26631             std::memset(ptrd,0,vsiz*sizeof(double));
26632             return cimg::type<double>::nan();
26633           }
26634         std::memset(ptrd,0,vsiz*sizeof(double));
26635         return cimg::type<double>::nan();
26636       }
26637 
26638       static double mp_Ixyz(_cimg_math_parser& mp) {
26639         double *ptrd = &_mp_arg(1) + 1;
26640         const unsigned int
26641           interpolation = (unsigned int)_mp_arg(5),
26642           boundary_conditions = (unsigned int)_mp_arg(6),
26643           vsiz = (unsigned int)mp.opcode[7];
26644         const CImg<T> &img = mp.imgin;
26645         const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4);
26646         const ulongT whd = (ulongT)img._width*img._height*img._depth;
26647         const T *ptrs;
26648         switch (interpolation) {
26649         case 2 : // Cubic interpolation
26650           switch (boundary_conditions) {
26651           case 3 : { // Mirror
26652             const float
26653               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
26654               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
26655               cx = mx<img.width()?mx:w2 - mx - 1,
26656               cy = my<img.height()?my:h2 - my - 1,
26657               cz = mz<img.depth()?mz:d2 - mz - 1;
26658             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
26659           } break;
26660           case 2 : // Periodic
26661             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
26662             break;
26663           case 1 : // Neumann
26664             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
26665             break;
26666           default : // Dirichlet
26667             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
26668           } break;
26669         case 1 : // Linear interpolation
26670           switch (boundary_conditions) {
26671           case 3 : { // Mirror
26672             const float
26673               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
26674               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
26675               cx = mx<img.width()?mx:w2 - mx - 1,
26676               cy = my<img.height()?my:h2 - my - 1,
26677               cz = mz<img.depth()?mz:d2 - mz - 1;
26678             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
26679           } break;
26680           case 2 : // Periodic
26681             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
26682             break;
26683           case 1 : // Neumann
26684             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
26685             break;
26686           default : // Dirichlet
26687             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
26688           } break;
26689         default : // Nearest neighbor interpolation
26690           switch (boundary_conditions) {
26691           case 3 : { // Mirror
26692             const int
26693               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
26694               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
26695               cx = mx<img.width()?mx:w2 - mx - 1,
26696               cy = my<img.height()?my:h2 - my - 1,
26697               cz = mz<img.depth()?mz:d2 - mz - 1;
26698             ptrs = &img(cx,cy,cz);
26699             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
26700           } break;
26701           case 2 : { // Periodic
26702             const int
26703               cx = (int)cimg::mod(x,(double)img._width),
26704               cy = (int)cimg::mod(y,(double)img._height),
26705               cz = (int)cimg::mod(z,(double)img._depth);
26706             ptrs = &img(cx,cy,cz);
26707             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
26708           } break;
26709           case 1 : { // Neumann
26710             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
26711             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
26712           } break;
26713           default : // Dirichlet
26714             if (img.containsXYZC((int)x,(int)y,(int)z)) {
26715               ptrs = &img((int)x,(int)y,(int)z);
26716               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
26717             } else std::memset(ptrd,0,vsiz*sizeof(double));
26718           }
26719         }
26720         return cimg::type<double>::nan();
26721       }
26722 
26723       static double mp_Joff(_cimg_math_parser& mp) {
26724         double *ptrd = &_mp_arg(1) + 1;
26725         const unsigned int
26726           boundary_conditions = (unsigned int)_mp_arg(3),
26727           vsiz = (unsigned int)mp.opcode[4];
26728         const CImg<T> &img = mp.imgin;
26729         const int
26730           ox = (int)mp.mem[_cimg_mp_slot_x],
26731           oy = (int)mp.mem[_cimg_mp_slot_y],
26732           oz = (int)mp.mem[_cimg_mp_slot_z];
26733         const longT
26734           off = img.offset(ox,oy,oz) + (longT)_mp_arg(2),
26735           whd = (longT)img.width()*img.height()*img.depth();
26736         const T *ptrs;
26737         if (off>=0 && off<whd) {
26738           ptrs = &img[off];
26739           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26740           return cimg::type<double>::nan();
26741         }
26742         if (img._data) switch (boundary_conditions) {
26743           case 3 : { // Mirror
26744             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
26745             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
26746             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26747             return cimg::type<double>::nan();
26748           }
26749           case 2 : // Periodic
26750             ptrs = &img[cimg::mod(off,whd)];
26751             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26752             return cimg::type<double>::nan();
26753           case 1 : // Neumann
26754             ptrs = off<0?&img[0]:&img[whd - 1];
26755             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
26756             return cimg::type<double>::nan();
26757           default : // Dirichlet
26758             std::memset(ptrd,0,vsiz*sizeof(double));
26759             return cimg::type<double>::nan();
26760           }
26761         std::memset(ptrd,0,vsiz*sizeof(double));
26762         return cimg::type<double>::nan();
26763       }
26764 
26765       static double mp_Jxyz(_cimg_math_parser& mp) {
26766         double *ptrd = &_mp_arg(1) + 1;
26767         const unsigned int
26768           interpolation = (unsigned int)_mp_arg(5),
26769           boundary_conditions = (unsigned int)_mp_arg(6),
26770           vsiz = (unsigned int)mp.opcode[7];
26771         const CImg<T> &img = mp.imgin;
26772         const double
26773           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
26774           x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4);
26775         const ulongT whd = (ulongT)img._width*img._height*img._depth;
26776         const T *ptrs;
26777         switch (interpolation) {
26778         case 2 : // Cubic interpolation
26779           switch (boundary_conditions) {
26780           case 3 : { // Mirror
26781             const float
26782               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
26783               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
26784               cx = mx<img.width()?mx:w2 - mx - 1,
26785               cy = my<img.height()?my:h2 - my - 1,
26786               cz = mz<img.depth()?mz:d2 - mz - 1;
26787             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
26788           } break;
26789           case 2 : // Periodic
26790             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
26791             break;
26792           case 1 : // Neumann
26793             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
26794             break;
26795           default : // Dirichlet
26796             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
26797           } break;
26798         case 1 : // Linear interpolation
26799           switch (boundary_conditions) {
26800           case 3 : { // Mirror
26801             const float
26802               w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
26803               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
26804               cx = mx<img.width()?mx:w2 - mx - 1,
26805               cy = my<img.height()?my:h2 - my - 1,
26806               cz = mz<img.depth()?mz:d2 - mz - 1;
26807             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
26808           } break;
26809           case 2 : // Periodic
26810             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
26811             break;
26812           case 1 : // Neumann
26813             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
26814             break;
26815           default : // Dirichlet
26816             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
26817           } break;
26818         default : // Nearest neighbor interpolation
26819           switch (boundary_conditions) {
26820           case 3 : { // Mirror
26821             const int
26822               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
26823               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
26824               cx = mx<img.width()?mx:w2 - mx - 1,
26825               cy = my<img.height()?my:h2 - my - 1,
26826               cz = mz<img.depth()?mz:d2 - mz - 1;
26827             ptrs = &img(cx,cy,cz);
26828             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
26829           } break;
26830           case 2 : { // Periodic
26831             const int
26832               cx = (int)cimg::mod(x,(double)img._width),
26833               cy = (int)cimg::mod(y,(double)img._height),
26834               cz = (int)cimg::mod(z,(double)img._depth);
26835             ptrs = &img(cx,cy,cz);
26836             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
26837           } break;
26838           case 1 : { // Neumann
26839             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
26840             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
26841           } break;
26842           default : // Dirichlet
26843             if (img.containsXYZC((int)x,(int)y,(int)z)) {
26844               ptrs = &img((int)x,(int)y,(int)z);
26845               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
26846             } else std::memset(ptrd,0,vsiz*sizeof(double));
26847           }
26848         }
26849         return cimg::type<double>::nan();
26850       }
26851 
26852 #undef _mp_arg
26853 
26854     }; // struct _cimg_math_parser {}
26855 
26856 #define _cimg_create_pointwise_functions(name,func,min_size) \
26857     CImg<T>& name() { \
26858       if (is_empty()) return *this; \
26859       cimg_openmp_for(*this,func((double)*ptr),min_size); \
26860       return *this; \
26861     } \
26862     CImg<Tfloat> get_##name() const { \
26863       return CImg<Tfloat>(*this,false).name(); \
26864     }
26865 
26866     //! Compute the square value of each pixel value.
26867     /**
26868        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$.
26869        \note
26870        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26871        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26872        \par Example
26873        \code
26874        const CImg<float> img("reference.jpg");
26875        (img,img.get_sqr().normalize(0,255)).display();
26876        \endcode
26877        \image html ref_sqr.jpg
26878     **/
26879     _cimg_create_pointwise_functions(sqr,cimg::sqr,524288)
26880 
26881     //! Compute the square root of each pixel value.
26882     /**
26883        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$.
26884        \note
26885        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26886        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26887        \par Example
26888        \code
26889        const CImg<float> img("reference.jpg");
26890        (img,img.get_sqrt().normalize(0,255)).display();
26891        \endcode
26892        \image html ref_sqrt.jpg
26893     **/
26894     _cimg_create_pointwise_functions(sqrt,std::sqrt,8192)
26895 
26896     //! Compute the exponential of each pixel value.
26897     /**
26898        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$.
26899        \note
26900        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26901        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26902     **/
26903     _cimg_create_pointwise_functions(exp,std::exp,4096)
26904 
26905     //! Compute the logarithm of each pixel value.
26906     /**
26907        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm
26908        \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
26909        \note
26910        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26911        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26912     **/
26913     _cimg_create_pointwise_functions(log,std::log,262144)
26914 
26915     //! Compute the base-2 logarithm of each pixel value.
26916     /**
26917        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm
26918        \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
26919        \note
26920        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26921        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26922     **/
26923     _cimg_create_pointwise_functions(log2,cimg::log2,4096)
26924 
26925     //! Compute the base-10 logarithm of each pixel value.
26926     /**
26927        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm
26928        \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
26929        \note
26930        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26931        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26932     **/
26933     _cimg_create_pointwise_functions(log10,std::log10,4096)
26934 
26935     //! Compute the absolute value of each pixel value.
26936     /**
26937        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$.
26938        \note
26939        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26940        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26941     **/
26942     _cimg_create_pointwise_functions(abs,cimg::abs,524288)
26943 
26944     //! Compute the sign of each pixel value.
26945     /**
26946        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign
26947        \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
26948        \note
26949        - The sign is set to:
26950          - \c 1 if pixel value is strictly positive.
26951          - \c -1 if pixel value is strictly negative.
26952          - \c 0 if pixel value is equal to \c 0.
26953        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26954        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26955     **/
26956     _cimg_create_pointwise_functions(sign,cimg::sign,32768)
26957 
26958     //! Compute the cosine of each pixel value.
26959     /**
26960        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$.
26961        \note
26962        - Pixel values are regarded as being in \e radian.
26963        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26964        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26965     **/
26966     _cimg_create_pointwise_functions(cos,std::cos,8192)
26967 
26968     //! Compute the sine of each pixel value.
26969     /**
26970        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$.
26971        \note
26972        - Pixel values are regarded as being in \e radian.
26973        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26974        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26975     **/
26976     _cimg_create_pointwise_functions(sin,std::sin,8192)
26977 
26978     //! Compute the sinc of each pixel value.
26979     /**
26980        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc
26981        \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
26982        \note
26983        - Pixel values are regarded as being exin \e radian.
26984        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26985        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26986     **/
26987     _cimg_create_pointwise_functions(sinc,cimg::sinc,2048)
26988 
26989     //! Compute the tangent of each pixel value.
26990     /**
26991        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$.
26992        \note
26993        - Pixel values are regarded as being exin \e radian.
26994        - The \inplace of this method statically casts the computed values to the pixel type \c T.
26995        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
26996     **/
26997     _cimg_create_pointwise_functions(tan,std::tan,2048)
26998 
26999     //! Compute the hyperbolic cosine of each pixel value.
27000     /**
27001        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine
27002        \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
27003        \note
27004        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27005        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27006     **/
27007     _cimg_create_pointwise_functions(cosh,std::cosh,2048)
27008 
27009     //! Compute the hyperbolic sine of each pixel value.
27010     /**
27011        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine
27012        \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
27013        \note
27014        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27015        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27016     **/
27017     _cimg_create_pointwise_functions(sinh,std::sinh,2048)
27018 
27019     //! Compute the hyperbolic tangent of each pixel value.
27020     /**
27021        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent
27022        \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
27023        \note
27024        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27025        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27026     **/
27027     _cimg_create_pointwise_functions(tanh,std::tanh,2048)
27028 
27029     //! Compute the arccosine of each pixel value.
27030     /**
27031        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine
27032        \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
27033        \note
27034        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27035        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27036     **/
27037     _cimg_create_pointwise_functions(acos,std::acos,8192)
27038 
27039     //! Compute the arcsine of each pixel value.
27040     /**
27041        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine
27042        \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
27043        \note
27044        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27045        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27046     **/
27047     _cimg_create_pointwise_functions(asin,std::asin,8192)
27048 
27049     //! Compute the arctangent of each pixel value.
27050     /**
27051        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent
27052        \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
27053        \note
27054        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27055        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27056     **/
27057     _cimg_create_pointwise_functions(atan,std::atan,8192)
27058 
27059     //! Compute the arctangent2 of each pixel value.
27060     /**
27061        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2
27062        \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
27063        \param img Image whose pixel values specify the second argument of the \c atan2() function.
27064        \note
27065        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27066        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27067        \par Example
27068        \code
27069        const CImg<float>
27070           img_x(100,100,1,1,"x-w/2",false),   // Define an horizontal centered gradient, from '-width/2' to 'width/2'
27071           img_y(100,100,1,1,"y-h/2",false),   // Define a vertical centered gradient, from '-height/2' to 'height/2'
27072           img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value
27073        (img_x,img_y,img_atan2).display();
27074        \endcode
27075     **/
27076     template<typename t>
27077     CImg<T>& atan2(const CImg<t>& img) {
27078       const ulongT siz = size(), isiz = img.size();
27079       if (siz && isiz) {
27080         if (is_overlapped(img)) return atan2(+img);
27081         T *ptrd = _data, *const ptre = _data + siz;
27082         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27083           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27084             *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
27085         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
27086       }
27087       return *this;
27088     }
27089 
27090     //! Compute the arctangent2 of each pixel value \newinstance.
27091     template<typename t>
27092     CImg<Tfloat> get_atan2(const CImg<t>& img) const {
27093       return CImg<Tfloat>(*this,false).atan2(img);
27094     }
27095 
27096     //! Compute the hyperbolic arccosine of each pixel value.
27097     /**
27098        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh
27099        \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$.
27100        \note
27101        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27102        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27103     **/
27104     _cimg_create_pointwise_functions(acosh,cimg::acosh,8192)
27105 
27106     //! Compute the hyperbolic arcsine of each pixel value.
27107     /**
27108        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine
27109        \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$.
27110        \note
27111        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27112        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27113     **/
27114     _cimg_create_pointwise_functions(asinh,cimg::asinh,8192)
27115 
27116     //! Compute the hyperbolic arctangent of each pixel value.
27117     /**
27118        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent
27119        \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$.
27120        \note
27121        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27122        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27123     **/
27124     _cimg_create_pointwise_functions(atanh,cimg::atanh,8192)
27125 
27126     //! In-place pointwise multiplication.
27127     /**
27128        Compute the pointwise multiplication between the image instance and the specified input image \c img.
27129        \param img Input image, as the second operand of the multiplication.
27130        \note
27131        - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication
27132          instead of an addition.
27133        - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
27134        \par Example
27135        \code
27136        CImg<float>
27137          img("reference.jpg"),
27138          shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
27139        shade.normalize(0,1);
27140        (img,shade,img.get_mul(shade)).display();
27141        \endcode
27142     **/
27143     template<typename t>
27144     CImg<T>& mul(const CImg<t>& img) {
27145       const ulongT siz = size(), isiz = img.size();
27146       if (siz && isiz) {
27147         if (is_overlapped(img)) return mul(+img);
27148         T *ptrd = _data, *const ptre = _data + siz;
27149         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27150           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27151             *ptrd = (T)(*ptrd * *(ptrs++));
27152         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
27153       }
27154       return *this;
27155     }
27156 
27157     //! In-place pointwise multiplication \newinstance.
27158     template<typename t>
27159     CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
27160       return CImg<_cimg_Tt>(*this,false).mul(img);
27161     }
27162 
27163     //! In-place pointwise division.
27164     /**
27165        Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
27166     **/
27167     template<typename t>
27168     CImg<T>& div(const CImg<t>& img) {
27169       const ulongT siz = size(), isiz = img.size();
27170       if (siz && isiz) {
27171         if (is_overlapped(img)) return div(+img);
27172         T *ptrd = _data, *const ptre = _data + siz;
27173         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27174           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27175             *ptrd = (T)(*ptrd / *(ptrs++));
27176         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
27177       }
27178       return *this;
27179     }
27180 
27181     //! In-place pointwise division \newinstance.
27182     template<typename t>
27183     CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
27184       return CImg<_cimg_Tt>(*this,false).div(img);
27185     }
27186 
27187     //! Raise each pixel value to a specified power.
27188     /**
27189        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$.
27190        \param p Exponent value.
27191        \note
27192        - The \inplace of this method statically casts the computed values to the pixel type \c T.
27193        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
27194        \par Example
27195        \code
27196        const CImg<float>
27197          img0("reference.jpg"),           // Load reference color image
27198          img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8
27199          img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5
27200        (img0,img1,img2).display();
27201        \endcode
27202     **/
27203     CImg<T>& pow(const double p) {
27204       if (is_empty()) return *this;
27205       if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; }
27206       if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; }
27207       if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; }
27208       if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; }
27209       if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; }
27210       if (p==0) return fill((T)1);
27211       if (p==0.5) return sqrt();
27212       if (p==1) return *this;
27213       if (p==2) return sqr();
27214       if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; }
27215       if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; }
27216       cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024);
27217       return *this;
27218     }
27219 
27220     //! Raise each pixel value to a specified power \newinstance.
27221     CImg<Tfloat> get_pow(const double p) const {
27222       return CImg<Tfloat>(*this,false).pow(p);
27223     }
27224 
27225     //! Raise each pixel value to a power, specified from an expression.
27226     /**
27227        Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
27228     **/
27229     CImg<T>& pow(const char *const expression) {
27230       return pow((+*this)._fill(expression,true,1,0,0,"pow",this));
27231     }
27232 
27233     //! Raise each pixel value to a power, specified from an expression \newinstance.
27234     CImg<Tfloat> get_pow(const char *const expression) const {
27235       return CImg<Tfloat>(*this,false).pow(expression);
27236     }
27237 
27238     //! Raise each pixel value to a power, pointwisely specified from another image.
27239     /**
27240        Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
27241     **/
27242     template<typename t>
27243     CImg<T>& pow(const CImg<t>& img) {
27244       const ulongT siz = size(), isiz = img.size();
27245       if (siz && isiz) {
27246         if (is_overlapped(img)) return pow(+img);
27247         T *ptrd = _data, *const ptre = _data + siz;
27248         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27249           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27250             *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
27251         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
27252       }
27253       return *this;
27254     }
27255 
27256     //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
27257     template<typename t>
27258     CImg<Tfloat> get_pow(const CImg<t>& img) const {
27259       return CImg<Tfloat>(*this,false).pow(img);
27260     }
27261 
27262     //! Compute the bitwise left rotation of each pixel value.
27263     /**
27264        Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
27265     **/
27266     CImg<T>& rol(const unsigned int n=1) {
27267       if (is_empty()) return *this;
27268       cimg_openmp_for(*this,cimg::rol(*ptr,n),32768);
27269       return *this;
27270     }
27271 
27272     //! Compute the bitwise left rotation of each pixel value \newinstance.
27273     CImg<T> get_rol(const unsigned int n=1) const {
27274       return (+*this).rol(n);
27275     }
27276 
27277     //! Compute the bitwise left rotation of each pixel value.
27278     /**
27279        Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
27280     **/
27281     CImg<T>& rol(const char *const expression) {
27282       return rol((+*this)._fill(expression,true,1,0,0,"rol",this));
27283     }
27284 
27285     //! Compute the bitwise left rotation of each pixel value \newinstance.
27286     CImg<T> get_rol(const char *const expression) const {
27287       return (+*this).rol(expression);
27288     }
27289 
27290     //! Compute the bitwise left rotation of each pixel value.
27291     /**
27292        Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
27293     **/
27294     template<typename t>
27295     CImg<T>& rol(const CImg<t>& img) {
27296       const ulongT siz = size(), isiz = img.size();
27297       if (siz && isiz) {
27298         if (is_overlapped(img)) return rol(+img);
27299         T *ptrd = _data, *const ptre = _data + siz;
27300         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27301           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27302             *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
27303         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
27304       }
27305       return *this;
27306     }
27307 
27308     //! Compute the bitwise left rotation of each pixel value \newinstance.
27309     template<typename t>
27310     CImg<T> get_rol(const CImg<t>& img) const {
27311       return (+*this).rol(img);
27312     }
27313 
27314     //! Compute the bitwise right rotation of each pixel value.
27315     /**
27316        Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
27317     **/
27318     CImg<T>& ror(const unsigned int n=1) {
27319       if (is_empty()) return *this;
27320       cimg_openmp_for(*this,cimg::ror(*ptr,n),32768);
27321       return *this;
27322     }
27323 
27324     //! Compute the bitwise right rotation of each pixel value \newinstance.
27325     CImg<T> get_ror(const unsigned int n=1) const {
27326       return (+*this).ror(n);
27327     }
27328 
27329     //! Compute the bitwise right rotation of each pixel value.
27330     /**
27331        Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
27332     **/
27333     CImg<T>& ror(const char *const expression) {
27334       return ror((+*this)._fill(expression,true,1,0,0,"ror",this));
27335     }
27336 
27337     //! Compute the bitwise right rotation of each pixel value \newinstance.
27338     CImg<T> get_ror(const char *const expression) const {
27339       return (+*this).ror(expression);
27340     }
27341 
27342     //! Compute the bitwise right rotation of each pixel value.
27343     /**
27344        Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
27345     **/
27346     template<typename t>
27347     CImg<T>& ror(const CImg<t>& img) {
27348       const ulongT siz = size(), isiz = img.size();
27349       if (siz && isiz) {
27350         if (is_overlapped(img)) return ror(+img);
27351         T *ptrd = _data, *const ptre = _data + siz;
27352         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27353           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27354             *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
27355         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
27356       }
27357       return *this;
27358     }
27359 
27360     //! Compute the bitwise right rotation of each pixel value \newinstance.
27361     template<typename t>
27362     CImg<T> get_ror(const CImg<t>& img) const {
27363       return (+*this).ror(img);
27364     }
27365 
27366     //! Pointwise min operator between instance image and a value.
27367     /**
27368        \param val Value used as the reference argument of the min operator.
27369        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27370        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
27371      **/
27372     CImg<T>& min(const T& value) {
27373       if (is_empty()) return *this;
27374       cimg_openmp_for(*this,std::min(*ptr,value),65536);
27375       return *this;
27376     }
27377 
27378     //! Pointwise min operator between instance image and a value \newinstance.
27379     CImg<T> get_min(const T& value) const {
27380       return (+*this).min(value);
27381     }
27382 
27383     //! Pointwise min operator between two images.
27384     /**
27385        \param img Image used as the reference argument of the min operator.
27386        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27387        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
27388      **/
27389     template<typename t>
27390     CImg<T>& min(const CImg<t>& img) {
27391       const ulongT siz = size(), isiz = img.size();
27392       if (siz && isiz) {
27393         if (is_overlapped(img)) return min(+img);
27394         T *ptrd = _data, *const ptre = _data + siz;
27395         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27396           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27397             *ptrd = std::min((T)*(ptrs++),*ptrd);
27398         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd);
27399       }
27400       return *this;
27401     }
27402 
27403     //! Pointwise min operator between two images \newinstance.
27404     template<typename t>
27405     CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
27406       return CImg<_cimg_Tt>(*this,false).min(img);
27407     }
27408 
27409     //! Pointwise min operator between an image and an expression.
27410     /**
27411        \param expression Math formula as a C-string.
27412        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27413        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
27414     **/
27415     CImg<T>& min(const char *const expression) {
27416       return min((+*this)._fill(expression,true,1,0,0,"min",this));
27417     }
27418 
27419     //! Pointwise min operator between an image and an expression \newinstance.
27420     CImg<Tfloat> get_min(const char *const expression) const {
27421       return CImg<Tfloat>(*this,false).min(expression);
27422     }
27423 
27424     //! Pointwise max operator between instance image and a value.
27425     /**
27426        \param val Value used as the reference argument of the max operator.
27427        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27428        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
27429      **/
27430     CImg<T>& max(const T& value) {
27431       if (is_empty()) return *this;
27432       cimg_openmp_for(*this,std::max(*ptr,value),65536);
27433       return *this;
27434     }
27435 
27436     //! Pointwise max operator between instance image and a value \newinstance.
27437     CImg<T> get_max(const T& value) const {
27438       return (+*this).max(value);
27439     }
27440 
27441     //! Pointwise max operator between two images.
27442     /**
27443        \param img Image used as the reference argument of the max operator.
27444        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27445        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
27446      **/
27447     template<typename t>
27448     CImg<T>& max(const CImg<t>& img) {
27449       const ulongT siz = size(), isiz = img.size();
27450       if (siz && isiz) {
27451         if (is_overlapped(img)) return max(+img);
27452         T *ptrd = _data, *const ptre = _data + siz;
27453         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27454           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27455             *ptrd = std::max((T)*(ptrs++),*ptrd);
27456         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd);
27457       }
27458       return *this;
27459     }
27460 
27461     //! Pointwise max operator between two images \newinstance.
27462     template<typename t>
27463     CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
27464       return CImg<_cimg_Tt>(*this,false).max(img);
27465     }
27466 
27467     //! Pointwise max operator between an image and an expression.
27468     /**
27469        \param expression Math formula as a C-string.
27470        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27471        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
27472     **/
27473     CImg<T>& max(const char *const expression) {
27474       return max((+*this)._fill(expression,true,1,0,0,"max",this));
27475     }
27476 
27477     //! Pointwise max operator between an image and an expression \newinstance.
27478     CImg<Tfloat> get_max(const char *const expression) const {
27479       return CImg<Tfloat>(*this,false).max(expression);
27480     }
27481 
27482     //! Pointwise minabs operator between instance image and a value.
27483     /**
27484        \param val Value used as the reference argument of the minabs operator.
27485        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27486        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
27487      **/
27488     CImg<T>& minabs(const T& value) {
27489       if (is_empty()) return *this;
27490       const T absvalue = cimg::abs(value);
27491       cimg_openmp_for(*this,cimg::minabs(*ptr,value,absvalue),65536);
27492       return *this;
27493     }
27494 
27495     //! Pointwise minabs operator between instance image and a value \newinstance.
27496     CImg<T> get_minabs(const T& value) const {
27497       return (+*this).minabs(value);
27498     }
27499 
27500     //! Pointwise minabs operator between two images.
27501     /**
27502        \param img Image used as the reference argument of the minabs operator.
27503        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27504        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
27505      **/
27506     template<typename t>
27507     CImg<T>& minabs(const CImg<t>& img) {
27508       const ulongT siz = size(), isiz = img.size();
27509       if (siz && isiz) {
27510         if (is_overlapped(img)) return minabs(+img);
27511         T *ptrd = _data, *const ptre = _data + siz;
27512         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27513           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27514             *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
27515         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
27516       }
27517       return *this;
27518     }
27519 
27520     //! Pointwise minabs operator between two images \newinstance.
27521     template<typename t>
27522     CImg<_cimg_Tt> get_minabs(const CImg<t>& img) const {
27523       return CImg<_cimg_Tt>(*this,false).minabs(img);
27524     }
27525 
27526     //! Pointwise minabs operator between an image and an expression.
27527     /**
27528        \param expression Math formula as a C-string.
27529        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27530        \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
27531     **/
27532     CImg<T>& minabs(const char *const expression) {
27533       return minabs((+*this)._fill(expression,true,1,0,0,"minabs",this));
27534     }
27535 
27536     //! Pointwise minabs operator between an image and an expression \newinstance.
27537     CImg<Tfloat> get_minabs(const char *const expression) const {
27538       return CImg<Tfloat>(*this,false).minabs(expression);
27539     }
27540 
27541     //! Pointwise maxabs operator between instance image and a value.
27542     /**
27543        \param val Value used as the reference argument of the maxabs operator.
27544        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27545        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
27546      **/
27547     CImg<T>& maxabs(const T& value) {
27548       if (is_empty()) return *this;
27549       const T absvalue = cimg::abs(value);
27550       cimg_openmp_for(*this,cimg::maxabs(*ptr,value,absvalue),65536);
27551       return *this;
27552     }
27553 
27554     //! Pointwise maxabs operator between instance image and a value \newinstance.
27555     CImg<T> get_maxabs(const T& value) const {
27556       return (+*this).maxabs(value);
27557     }
27558 
27559     //! Pointwise maxabs operator between two images.
27560     /**
27561        \param img Image used as the reference argument of the maxabs operator.
27562        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27563        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
27564      **/
27565     template<typename t>
27566     CImg<T>& maxabs(const CImg<t>& img) {
27567       const ulongT siz = size(), isiz = img.size();
27568       if (siz && isiz) {
27569         if (is_overlapped(img)) return maxabs(+img);
27570         T *ptrd = _data, *const ptre = _data + siz;
27571         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
27572           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
27573             *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
27574         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
27575       }
27576       return *this;
27577     }
27578 
27579     //! Pointwise maxabs operator between two images \newinstance.
27580     template<typename t>
27581     CImg<_cimg_Tt> get_maxabs(const CImg<t>& img) const {
27582       return CImg<_cimg_Tt>(*this,false).maxabs(img);
27583     }
27584 
27585     //! Pointwise maxabs operator between an image and an expression.
27586     /**
27587        \param expression Math formula as a C-string.
27588        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
27589        \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
27590     **/
27591     CImg<T>& maxabs(const char *const expression) {
27592       return maxabs((+*this)._fill(expression,true,1,0,0,"maxabs",this));
27593     }
27594 
27595     //! Pointwise maxabs operator between an image and an expression \newinstance.
27596     CImg<Tfloat> get_maxabs(const char *const expression) const {
27597       return CImg<Tfloat>(*this,false).maxabs(expression);
27598     }
27599 
27600     //! Return a reference to the minimum pixel value.
27601     /**
27602      **/
27603     T& min() {
27604       if (is_empty())
27605         throw CImgInstanceException(_cimg_instance
27606                                     "min(): Empty instance.",
27607                                     cimg_instance);
27608       T *ptr_min = _data;
27609       T min_value = *ptr_min;
27610       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
27611       return *ptr_min;
27612     }
27613 
27614     //! Return a reference to the minimum pixel value \const.
27615     const T& min() const {
27616       if (is_empty())
27617         throw CImgInstanceException(_cimg_instance
27618                                     "min(): Empty instance.",
27619                                     cimg_instance);
27620       const T *ptr_min = _data;
27621       T min_value = *ptr_min;
27622       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
27623       return *ptr_min;
27624     }
27625 
27626     //! Return a reference to the minimum pixel value in absolute value.
27627     /**
27628      **/
27629     T& minabs() {
27630       if (is_empty())
27631         throw CImgInstanceException(_cimg_instance
27632                                     "minabs(): Empty instance.",
27633                                     cimg_instance);
27634       T *ptr_minabs = _data;
27635       T minabs_value = *ptr_minabs;
27636       cimg_for(*this,ptrs,T) {
27637         const T ma = cimg::abs(*ptrs);
27638         if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
27639       }
27640       return *ptr_minabs;
27641     }
27642 
27643     //! Return a reference to the minimum pixel value in absolute value \const.
27644     const T& minabs() const {
27645       if (is_empty())
27646         throw CImgInstanceException(_cimg_instance
27647                                     "minabs(): Empty instance.",
27648                                     cimg_instance);
27649       const T *ptr_minabs = _data;
27650       T minabs_value = *ptr_minabs;
27651       cimg_for(*this,ptrs,T) {
27652         const T ma = cimg::abs(*ptrs);
27653         if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
27654       }
27655       return *ptr_minabs;
27656     }
27657 
27658     //! Return a reference to the maximum pixel value.
27659     /**
27660      **/
27661     T& max() {
27662       if (is_empty())
27663         throw CImgInstanceException(_cimg_instance
27664                                     "max(): Empty instance.",
27665                                     cimg_instance);
27666       T *ptr_max = _data;
27667       T max_value = *ptr_max;
27668       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
27669       return *ptr_max;
27670     }
27671 
27672     //! Return a reference to the maximum pixel value \const.
27673     const T& max() const {
27674       if (is_empty())
27675         throw CImgInstanceException(_cimg_instance
27676                                     "max(): Empty instance.",
27677                                     cimg_instance);
27678       const T *ptr_max = _data;
27679       T max_value = *ptr_max;
27680       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
27681       return *ptr_max;
27682     }
27683 
27684     //! Return a reference to the maximum pixel value in absolute value.
27685     /**
27686      **/
27687     T& maxabs() {
27688       if (is_empty())
27689         throw CImgInstanceException(_cimg_instance
27690                                     "maxabs(): Empty instance.",
27691                                     cimg_instance);
27692       T *ptr_maxabs = _data;
27693       T maxabs_value = *ptr_maxabs;
27694       cimg_for(*this,ptrs,T) {
27695         const T ma = cimg::abs(*ptrs);
27696         if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
27697       }
27698       return *ptr_maxabs;
27699     }
27700 
27701     //! Return a reference to the maximum pixel value in absolute value \const.
27702     const T& maxabs() const {
27703       if (is_empty())
27704         throw CImgInstanceException(_cimg_instance
27705                                     "maxabs(): Empty instance.",
27706                                     cimg_instance);
27707       const T *ptr_maxabs = _data;
27708       T maxabs_value = *ptr_maxabs;
27709       cimg_for(*this,ptrs,T) {
27710         const T ma = cimg::abs(*ptrs);
27711         if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
27712       }
27713       return *ptr_maxabs;
27714     }
27715 
27716     //! Return a reference to the minimum pixel value as well as the maximum pixel value.
27717     /**
27718        \param[out] max_val Maximum pixel value.
27719     **/
27720     template<typename t>
27721     T& min_max(t& max_val) {
27722       if (is_empty())
27723         throw CImgInstanceException(_cimg_instance
27724                                     "min_max(): Empty instance.",
27725                                     cimg_instance);
27726       T *ptr_min = _data;
27727       T min_value = *ptr_min, max_value = min_value;
27728       cimg_for(*this,ptrs,T) {
27729         const T val = *ptrs;
27730         if (val<min_value) { min_value = val; ptr_min = ptrs; }
27731         if (val>max_value) max_value = val;
27732       }
27733       max_val = (t)max_value;
27734       return *ptr_min;
27735     }
27736 
27737     //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
27738     template<typename t>
27739     const T& min_max(t& max_val) const {
27740       if (is_empty())
27741         throw CImgInstanceException(_cimg_instance
27742                                     "min_max(): Empty instance.",
27743                                     cimg_instance);
27744       const T *ptr_min = _data;
27745       T min_value = *ptr_min, max_value = min_value;
27746       cimg_for(*this,ptrs,T) {
27747         const T val = *ptrs;
27748         if (val<min_value) { min_value = val; ptr_min = ptrs; }
27749         if (val>max_value) max_value = val;
27750       }
27751       max_val = (t)max_value;
27752       return *ptr_min;
27753     }
27754 
27755     //! Return a reference to the maximum pixel value as well as the minimum pixel value.
27756     /**
27757        \param[out] min_val Minimum pixel value.
27758     **/
27759     template<typename t>
27760     T& max_min(t& min_val) {
27761       if (is_empty())
27762         throw CImgInstanceException(_cimg_instance
27763                                     "max_min(): Empty instance.",
27764                                     cimg_instance);
27765       T *ptr_max = _data;
27766       T max_value = *ptr_max, min_value = max_value;
27767       cimg_for(*this,ptrs,T) {
27768         const T val = *ptrs;
27769         if (val>max_value) { max_value = val; ptr_max = ptrs; }
27770         if (val<min_value) min_value = val;
27771       }
27772       min_val = (t)min_value;
27773       return *ptr_max;
27774     }
27775 
27776     //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
27777     template<typename t>
27778     const T& max_min(t& min_val) const {
27779       if (is_empty())
27780         throw CImgInstanceException(_cimg_instance
27781                                     "max_min(): Empty instance.",
27782                                     cimg_instance);
27783       const T *ptr_max = _data;
27784       T max_value = *ptr_max, min_value = max_value;
27785       cimg_for(*this,ptrs,T) {
27786         const T val = *ptrs;
27787         if (val>max_value) { max_value = val; ptr_max = ptrs; }
27788         if (val<min_value) min_value = val;
27789       }
27790       min_val = (t)min_value;
27791       return *ptr_max;
27792     }
27793 
27794     //! Return the kth smallest pixel value.
27795     /**
27796        \param k Rank of the smallest element searched.
27797     **/
27798     T kth_smallest(const ulongT k) const {
27799       if (is_empty())
27800         throw CImgInstanceException(_cimg_instance
27801                                     "kth_smallest(): Empty instance.",
27802                                     cimg_instance);
27803       if (k>=size()) return max();
27804       CImg<T> arr(*this,false);
27805       ulongT l = 0, ir = size() - 1;
27806       for ( ; ; ) {
27807         if (ir<=l + 1) {
27808           if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
27809           return arr[k];
27810         } else {
27811           const ulongT mid = (l + ir)>>1;
27812           cimg::swap(arr[mid],arr[l + 1]);
27813           if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
27814           if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]);
27815           if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]);
27816           ulongT i = l + 1, j = ir;
27817           const T pivot = arr[l + 1];
27818           for ( ; ; ) {
27819             do ++i; while (arr[i]<pivot);
27820             do --j; while (arr[j]>pivot);
27821             if (j<i) break;
27822             cimg::swap(arr[i],arr[j]);
27823           }
27824           arr[l + 1] = arr[j];
27825           arr[j] = pivot;
27826           if (j>=k) ir = j - 1;
27827           if (j<=k) l = i;
27828         }
27829       }
27830     }
27831 
27832     //! Return the median pixel value.
27833     /**
27834      **/
27835     T median() const {
27836       if (is_empty())
27837         throw CImgInstanceException(_cimg_instance
27838                                     "median(): Empty instance.",
27839                                     cimg_instance);
27840       const ulongT s = size();
27841       switch (s) {
27842       case 1 : return _data[0];
27843       case 2 : return cimg::median(_data[0],_data[1]);
27844       case 3 : return cimg::median(_data[0],_data[1],_data[2]);
27845       case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]);
27846       case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]);
27847       case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]);
27848       case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8],
27849                                     _data[9],_data[10],_data[11],_data[12]);
27850       }
27851       const T res = kth_smallest(s>>1);
27852       return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2);
27853     }
27854 
27855     //! Return the product of all the pixel values.
27856     /**
27857      **/
27858     double product() const {
27859       if (is_empty()) return 0;
27860       double res = 1;
27861       cimg_for(*this,ptrs,T) res*=(double)*ptrs;
27862       return res;
27863     }
27864 
27865     //! Return the sum of all the pixel values.
27866     /**
27867      **/
27868     double sum() const {
27869       double res = 0;
27870       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
27871       return res;
27872     }
27873 
27874     //! Return the average pixel value.
27875     /**
27876      **/
27877     double mean() const {
27878       double res = 0;
27879       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
27880       return res/size();
27881     }
27882 
27883     //! Return the variance of the pixel values.
27884     /**
27885        \param variance_method Method used to estimate the variance. Can be:
27886        - \c 0: Second moment, computed as
27887        \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 =
27888        1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
27889        with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
27890        - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
27891        - \c 2: Least median of squares.
27892        - \c 3: Least trimmed of squares.
27893     **/
27894     double variance(const unsigned int variance_method=1) const {
27895       double foo;
27896       return variance_mean(variance_method,foo);
27897     }
27898 
27899     //! Return the variance as well as the average of the pixel values.
27900     /**
27901        \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
27902        \param[out] mean Average pixel value.
27903     **/
27904     template<typename t>
27905     double variance_mean(const unsigned int variance_method, t& mean) const {
27906       if (is_empty())
27907         throw CImgInstanceException(_cimg_instance
27908                                     "variance_mean(): Empty instance.",
27909                                     cimg_instance);
27910 
27911       double variance = 0, average = 0;
27912       const ulongT siz = size();
27913       switch (variance_method) {
27914       case 0 : { // Least mean square (standard definition)
27915         double S = 0, S2 = 0;
27916         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
27917         variance = (S2 - S*S/siz)/siz;
27918         average = S;
27919       } break;
27920       case 1 : { // Least mean square (robust definition)
27921         double S = 0, S2 = 0;
27922         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
27923         variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
27924         average = S;
27925       } break;
27926       case 2 : { // Least Median of Squares (MAD)
27927         CImg<Tfloat> buf(*this,false);
27928         buf.sort();
27929         const ulongT siz2 = siz>>1;
27930         const double med_i = (double)buf[siz2];
27931         cimg_for(buf,ptrs,Tfloat) {
27932           const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
27933         }
27934         buf.sort();
27935         const double sig = (double)(1.4828*buf[siz2]);
27936         variance = sig*sig;
27937       } break;
27938       default : { // Least trimmed of Squares
27939         CImg<Tfloat> buf(*this,false);
27940         const ulongT siz2 = siz>>1;
27941         cimg_for(buf,ptrs,Tfloat) {
27942           const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
27943         }
27944         buf.sort();
27945         double a = 0;
27946         const Tfloat *ptrs = buf._data;
27947         for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++);
27948         const double sig = (double)(2.6477*std::sqrt(a/siz2));
27949         variance = sig*sig;
27950       }
27951       }
27952       mean = (t)(average/siz);
27953       return variance>0?variance:0;
27954     }
27955 
27956     //! Return estimated variance of the noise.
27957     /**
27958        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
27959        \note Because of structures such as edges in images it is
27960        recommended to use a robust variance estimation. The variance of the
27961        noise is estimated by computing the variance of the Laplacian \f$(\Delta
27962        I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
27963        \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
27964     **/
27965     double variance_noise(const unsigned int variance_method=2) const {
27966       if (is_empty())
27967         throw CImgInstanceException(_cimg_instance
27968                                     "variance_noise(): Empty instance.",
27969                                     cimg_instance);
27970 
27971       const ulongT siz = size();
27972       if (!siz || !_data) return 0;
27973       if (variance_method>1) { // Compute a scaled version of the Laplacian
27974         CImg<Tdouble> tmp(*this,false);
27975         if (_depth==1) {
27976           const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed
27977           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 &&
27978                                                          _spectrum>=2))
27979           cimg_forC(*this,c) {
27980             CImg_3x3(I,T);
27981             cimg_for3x3(*this,x,y,0,c,I,T) {
27982               tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn +
27983                                  (double)Icp - 4*(double)Icc);
27984             }
27985           }
27986         } else {
27987           const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed
27988           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 &&
27989                                                          _spectrum>=2))
27990           cimg_forC(*this,c) {
27991             CImg_3x3x3(I,T);
27992             cimg_for3x3x3(*this,x,y,z,c,I,T) {
27993               tmp(x,y,z,c) = cste*(
27994                                    (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc +
27995                                    (double)Iccn + (double)Iccp - 6*(double)Iccc);
27996             }
27997           }
27998         }
27999         return tmp.variance(variance_method);
28000       }
28001 
28002       // Version that doesn't need intermediate images.
28003       double variance = 0, S = 0, S2 = 0;
28004       if (_depth==1) {
28005         const double cste = 1./std::sqrt(20.);
28006         CImg_3x3(I,T);
28007         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
28008           const double val = cste*((double)Inc + (double)Ipc +
28009                                    (double)Icn + (double)Icp - 4*(double)Icc);
28010           S+=val; S2+=val*val;
28011         }
28012       } else {
28013         const double cste = 1./std::sqrt(42.);
28014         CImg_3x3x3(I,T);
28015         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
28016           const double val = cste *
28017             ((double)Incc + (double)Ipcc + (double)Icnc +
28018              (double)Icpc +
28019              (double)Iccn + (double)Iccp - 6*(double)Iccc);
28020           S+=val; S2+=val*val;
28021         }
28022       }
28023       if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
28024       else variance = (S2 - S*S/siz)/siz;
28025       return variance>0?variance:0;
28026     }
28027 
28028     //! Compute the MSE (Mean-Squared Error) between two images.
28029     /**
28030        \param img Image used as the second argument of the MSE operator.
28031     **/
28032     template<typename t>
28033     double MSE(const CImg<t>& img) const {
28034       if (img.size()!=size())
28035         throw CImgArgumentException(_cimg_instance
28036                                     "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
28037                                     cimg_instance,
28038                                     img._width,img._height,img._depth,img._spectrum,img._data);
28039       double vMSE = 0;
28040       const t* ptr2 = img._data;
28041       cimg_for(*this,ptr1,T) {
28042         const double diff = (double)*ptr1 - (double)*(ptr2++);
28043         vMSE+=diff*diff;
28044       }
28045       const ulongT siz = img.size();
28046       if (siz) vMSE/=siz;
28047       return vMSE;
28048     }
28049 
28050     //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
28051     /**
28052        \param img Image used as the second argument of the PSNR operator.
28053        \param max_value Maximum theoretical value of the signal.
28054      **/
28055     template<typename t>
28056     double PSNR(const CImg<t>& img, const double max_value=255) const {
28057       const double vMSE = (double)std::sqrt(MSE(img));
28058       return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());
28059     }
28060 
28061     //! Evaluate math formula.
28062     /**
28063        \param expression Math formula, as a C-string.
28064        \param x Value of the pre-defined variable \c x.
28065        \param y Value of the pre-defined variable \c y.
28066        \param z Value of the pre-defined variable \c z.
28067        \param c Value of the pre-defined variable \c c.
28068        \param list_inputs A list of input images attached to the specified math formula.
28069        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28070     **/
28071     double eval(const char *const expression,
28072                 const double x=0, const double y=0, const double z=0, const double c=0,
28073                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28074       return _eval(this,expression,x,y,z,c,list_inputs,list_outputs);
28075     }
28076 
28077     //! Evaluate math formula \const.
28078     double eval(const char *const expression,
28079                 const double x=0, const double y=0, const double z=0, const double c=0,
28080                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28081       return _eval(0,expression,x,y,z,c,list_inputs,list_outputs);
28082     }
28083 
28084     // Fast function to pre-evaluate common expressions.
28085     // (return 'true' in case of success, and set value of 'res').
28086     template<typename t>
28087     bool __eval(const char *const expression, t &res) const {
28088       if (!expression || !*expression) { res = (t)0; return true; }
28089       const char c = *expression;
28090       bool is_success = false;
28091       char c1, end;
28092       double val;
28093       if (c>='0' && c<='9') { // Possible value
28094         if (!expression[1]) { // Single digit
28095           res = (t)(c - '0');
28096           is_success = true;
28097         } else if (std::sscanf(expression,"%lf%c",&val,&end)==1) { // Single value
28098           res = (t)val;
28099           is_success = true;
28100         }
28101       } else if ((c=='+' || c=='-' || c=='!') && // +Value, -Value or !Value
28102                  (c1=expression[1])>='0' && c1<='0') {
28103         if (!expression[2]) { // [+-!] + Single digit
28104           const int ival = c1 - '0';
28105           res = (t)(c=='+'?ival:c=='-'?-ival:!ival);
28106           is_success = true;
28107         } else if (std::sscanf(expression + 1,"%lf%c",&val,&end)==1) { // [+-!] Single value
28108           res = (t)(c=='+'?val:c=='-'?-val:(double)!val);
28109           is_success = true;
28110         }
28111       } else if (!expression[1]) switch (*expression) { // Other common single-char expressions
28112         case 'w' : res = (t)_width; is_success = true; break;
28113         case 'h' : res = (t)_height; is_success = true; break;
28114         case 'd' : res = (t)_depth; is_success = true; break;
28115         case 's' : res = (t)_spectrum; is_success = true; break;
28116         case 'r' : res = (t)_is_shared; is_success = true; break;
28117         }
28118       return is_success;
28119     }
28120 
28121     double _eval(CImg<T> *const img_output, const char *const expression,
28122                  const double x, const double y, const double z, const double c,
28123                  const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
28124       if (!expression || !*expression) return 0;
28125       double _val = 0;
28126       if (__eval(expression,_val)) return _val;
28127       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
28128                                          *expression=='*' || *expression==':'),"eval",
28129                            *this,img_output,list_inputs,list_outputs,false);
28130       mp.begin_t();
28131       const double val = mp(x,y,z,c);
28132       mp.end_t();
28133       mp.end();
28134       return val;
28135     }
28136 
28137     //! Evaluate math formula.
28138     /**
28139        \param[out] output Contains values of output vector returned by the evaluated expression
28140          (or is empty if the returned type is scalar).
28141        \param expression Math formula, as a C-string.
28142        \param x Value of the pre-defined variable \c x.
28143        \param y Value of the pre-defined variable \c y.
28144        \param z Value of the pre-defined variable \c z.
28145        \param c Value of the pre-defined variable \c c.
28146        \param list_inputs A list of input images attached to the specified math formula.
28147        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28148     **/
28149     template<typename t>
28150     void eval(CImg<t> &output, const char *const expression,
28151               const double x=0, const double y=0, const double z=0, const double c=0,
28152               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28153       _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs);
28154     }
28155 
28156     //! Evaluate math formula \const.
28157     template<typename t>
28158     void eval(CImg<t>& output, const char *const expression,
28159               const double x=0, const double y=0, const double z=0, const double c=0,
28160               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28161       _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs);
28162     }
28163 
28164     template<typename t>
28165     void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression,
28166                const double x, const double y, const double z, const double c,
28167                const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
28168       if (!expression || !*expression) { output.assign(1); *output = 0; return; }
28169       double _val = 0;
28170       if (__eval(expression,_val)) { output.assign(1); *output = _val; return; }
28171       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
28172                                          *expression=='*' || *expression==':'),"eval",
28173                            *this,img_output,list_inputs,list_outputs,false);
28174       output.assign(1,std::max(1U,mp.result_dim));
28175       mp.begin_t();
28176       mp(x,y,z,c,output._data);
28177       mp.end_t();
28178       mp.end();
28179     }
28180 
28181     //! Evaluate math formula on a set of variables.
28182     /**
28183        \param expression Math formula, as a C-string.
28184        \param xyzc Set of values (x,y,z,c) used for the evaluation.
28185        \param list_inputs A list of input images attached to the specified math formula.
28186        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
28187     **/
28188     template<typename t>
28189     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
28190                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
28191       return _eval(this,expression,xyzc,list_inputs,list_outputs);
28192     }
28193 
28194     //! Evaluate math formula on a set of variables \const.
28195     template<typename t>
28196     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
28197                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28198       return _eval(0,expression,xyzc,list_inputs,list_outputs);
28199     }
28200 
28201     template<typename t>
28202     CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc,
28203                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
28204       CImg<doubleT> res(1,xyzc.size()/4);
28205       if (!expression || !*expression) return res.fill(0);
28206       _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false);
28207 
28208 #if cimg_use_openmp!=0
28209       unsigned int tid = 0;
28210       cimg_pragma_openmp(parallel if (res._height>=512))
28211       {
28212         _cimg_math_parser *_mp = 0;
28213         cimg_pragma_openmp(critical(_eval)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; }
28214         _cimg_math_parser &lmp = *_mp;
28215         cimg_pragma_openmp(barrier)
28216         lmp.begin_t();
28217         cimg_pragma_openmp(for)
28218           for (int i = 0; i<res.height(); ++i) {
28219             const unsigned int i4 = 4*i;
28220             const double
28221               x = (double)xyzc[i4], y = (double)xyzc[i4 + 1],
28222               z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3];
28223             res[i] = lmp(x,y,z,c);
28224           }
28225         lmp.end_t();
28226         cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
28227         if (&lmp!=&mp) delete &lmp;
28228       }
28229 #else
28230       mp.begin_t();
28231       const t *ps = xyzc._data;
28232       cimg_for(res,pd,double) {
28233         const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
28234         *pd = mp(x,y,z,c);
28235       }
28236       mp.end_t();
28237 #endif
28238       mp.end();
28239       return res;
28240     }
28241 
28242     //! Compute statistics vector from the pixel values.
28243     /**
28244        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
28245        \return Statistics vector as
28246          <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>.
28247     **/
28248     CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
28249       if (is_empty()) return CImg<doubleT>();
28250       const ulongT siz = size();
28251       const longT off_end = (longT)siz;
28252       double S = 0, S2 = 0, P = 1;
28253       longT offm = 0, offM = 0;
28254       T m = *_data, M = m;
28255 
28256       cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) {
28257         longT loffm = 0, loffM = 0;
28258         T lm = *_data, lM = lm;
28259         cimg_pragma_openmp(for)
28260         for (longT off = 0; off<off_end; ++off) {
28261           const T val = _data[off];
28262           const double _val = (double)val;
28263           if (val<lm) { lm = val; loffm = off; }
28264           if (val>lM) { lM = val; loffM = off; }
28265           S+=_val;
28266           S2+=_val*_val;
28267           P*=_val;
28268         }
28269         cimg_pragma_openmp(critical(get_stats)) {
28270           if (lm<m || (lm==m && loffm<offm)) { m = lm; offm = loffm; }
28271           if (lM>M || (lM==M && loffM<offM)) { M = lM; offM = loffM; }
28272         }
28273       }
28274 
28275       const double
28276         mean_value = S/siz,
28277         _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
28278         (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
28279          variance(variance_method)),
28280         variance_value = _variance_value>0?_variance_value:0;
28281       int
28282         xm = 0, ym = 0, zm = 0, cm = 0,
28283         xM = 0, yM = 0, zM = 0, cM = 0;
28284       contains(_data[offm],xm,ym,zm,cm);
28285       contains(_data[offM],xM,yM,zM,cM);
28286       return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value,
28287                                       (double)xm,(double)ym,(double)zm,(double)cm,
28288                                       (double)xM,(double)yM,(double)zM,(double)cM,
28289                                       S,P);
28290     }
28291 
28292     //! Compute statistics vector from the pixel values \inplace.
28293     CImg<T>& stats(const unsigned int variance_method=1) {
28294       return get_stats(variance_method).move_to(*this);
28295     }
28296 
28297     //@}
28298     //-------------------------------------
28299     //
28300     //! \name Vector / Matrix Operations
28301     //@{
28302     //-------------------------------------
28303 
28304     //! Compute norm of the image, viewed as a matrix.
28305     /**
28306        \param magnitude_type Norm type. Can be:
28307        - \c -1: Linf-norm
28308        - \c 0: L0-norm
28309        - \c 1: L1-norm
28310        - \c 2: L2-norm
28311     **/
28312     double magnitude(const int magnitude_type=2) const {
28313       if (is_empty())
28314         throw CImgInstanceException(_cimg_instance
28315                                     "magnitude(): Empty instance.",
28316                                     cimg_instance);
28317       const ulongT siz = size();
28318       double res = 0;
28319       switch (magnitude_type) {
28320       case -1 : {
28321         cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
28322       } break;
28323       case 1 : {
28324         cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
28325         for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]);
28326       } break;
28327       default : {
28328         cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
28329         for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]);
28330         res = (double)std::sqrt(res);
28331       }
28332       }
28333       return res;
28334     }
28335 
28336     //! Compute the trace of the image, viewed as a matrix.
28337     /**
28338      **/
28339     double trace() const {
28340       if (is_empty())
28341         throw CImgInstanceException(_cimg_instance
28342                                     "trace(): Empty instance.",
28343                                     cimg_instance);
28344       double res = 0;
28345       cimg_forX(*this,k) res+=(double)(*this)(k,k);
28346       return res;
28347     }
28348 
28349     //! Compute the determinant of the image, viewed as a matrix.
28350     /**
28351      **/
28352     double det() const {
28353       if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
28354         throw CImgInstanceException(_cimg_instance
28355                                     "det(): Instance is not a square matrix.",
28356                                     cimg_instance);
28357 
28358       switch (_width) {
28359       case 1 : return (double)((*this)(0,0));
28360       case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0));
28361       case 3 : {
28362         const double
28363           a = (double)_data[0], d = (double)_data[1], g = (double)_data[2],
28364           b = (double)_data[3], e = (double)_data[4], h = (double)_data[5],
28365           c = (double)_data[6], f = (double)_data[7], i = (double)_data[8];
28366         return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
28367       }
28368       default : {
28369         CImg<Tfloat> lu(*this,false);
28370         CImg<uintT> indx;
28371         bool d;
28372         lu._LU(indx,d);
28373         double res = d?(double)1:(double)-1;
28374         cimg_forX(lu,i) res*=lu(i,i);
28375         return res;
28376       }
28377       }
28378     }
28379 
28380     //! Compute the dot product between instance and argument, viewed as matrices.
28381     /**
28382        \param img Image used as a second argument of the dot product.
28383     **/
28384     template<typename t>
28385     double dot(const CImg<t>& img) const {
28386       const ulongT nb = std::min(size(),img.size());
28387       double res = 0;
28388       cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(nb,8192))
28389       for (longT off = 0; off<(longT)nb; ++off) res+=(double)_data[off]*(double)img[off];
28390       return res;
28391     }
28392 
28393     //! Get vector-valued pixel located at specified position.
28394     /**
28395        \param x X-coordinate of the pixel value.
28396        \param y Y-coordinate of the pixel value.
28397        \param z Z-coordinate of the pixel value.
28398     **/
28399     CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
28400       CImg<T> res;
28401       if (res._height!=_spectrum) res.assign(1,_spectrum);
28402       const ulongT whd = (ulongT)_width*_height*_depth;
28403       const T *ptrs = data(x,y,z);
28404       T *ptrd = res._data;
28405       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
28406       return res;
28407     }
28408 
28409     //! Get (square) matrix-valued pixel located at specified position.
28410     /**
28411        \param x X-coordinate of the pixel value.
28412        \param y Y-coordinate of the pixel value.
28413        \param z Z-coordinate of the pixel value.
28414        \note - The spectrum() of the image must be a square.
28415      **/
28416     CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
28417       const int n = (int)cimg::round(std::sqrt((double)_spectrum));
28418       const T *ptrs = data(x,y,z,0);
28419       const ulongT whd = (ulongT)_width*_height*_depth;
28420       CImg<T> res(n,n);
28421       T *ptrd = res._data;
28422       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
28423       return res;
28424     }
28425 
28426     //! Get tensor-valued pixel located at specified position.
28427     /**
28428        \param x X-coordinate of the pixel value.
28429        \param y Y-coordinate of the pixel value.
28430        \param z Z-coordinate of the pixel value.
28431     **/
28432     CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
28433       const T *ptrs = data(x,y,z,0);
28434       const ulongT whd = (ulongT)_width*_height*_depth;
28435       if (_spectrum==6)
28436         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd));
28437       if (_spectrum==3)
28438         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd));
28439       return tensor(*ptrs);
28440     }
28441 
28442     //! Set vector-valued pixel at specified position.
28443     /**
28444        \param vec Vector to put on the instance image.
28445        \param x X-coordinate of the pixel value.
28446        \param y Y-coordinate of the pixel value.
28447        \param z Z-coordinate of the pixel value.
28448     **/
28449     template<typename t>
28450     CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
28451       if (x<_width && y<_height && z<_depth) {
28452         const t *ptrs = vec._data;
28453         const ulongT whd = (ulongT)_width*_height*_depth;
28454         T *ptrd = data(x,y,z);
28455         for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) {
28456           *ptrd = (T)*(ptrs++); ptrd+=whd;
28457         }
28458       }
28459       return *this;
28460     }
28461 
28462     //! Set (square) matrix-valued pixel at specified position.
28463     /**
28464        \param mat Matrix to put on the instance image.
28465        \param x X-coordinate of the pixel value.
28466        \param y Y-coordinate of the pixel value.
28467        \param z Z-coordinate of the pixel value.
28468     **/
28469     template<typename t>
28470     CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
28471       return set_vector_at(mat,x,y,z);
28472     }
28473 
28474     //! Set tensor-valued pixel at specified position.
28475     /**
28476        \param ten Tensor to put on the instance image.
28477        \param x X-coordinate of the pixel value.
28478        \param y Y-coordinate of the pixel value.
28479        \param z Z-coordinate of the pixel value.
28480     **/
28481     template<typename t>
28482     CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
28483       T *ptrd = data(x,y,z,0);
28484       const ulongT siz = (ulongT)_width*_height*_depth;
28485       if (ten._height==2) {
28486         *ptrd = (T)ten[0]; ptrd+=siz;
28487         *ptrd = (T)ten[1]; ptrd+=siz;
28488         *ptrd = (T)ten[3];
28489       }
28490       else {
28491         *ptrd = (T)ten[0]; ptrd+=siz;
28492         *ptrd = (T)ten[1]; ptrd+=siz;
28493         *ptrd = (T)ten[2]; ptrd+=siz;
28494         *ptrd = (T)ten[4]; ptrd+=siz;
28495         *ptrd = (T)ten[5]; ptrd+=siz;
28496         *ptrd = (T)ten[8];
28497       }
28498       return *this;
28499     }
28500 
28501     //! Resize image to become a diagonal matrix.
28502     /**
28503        \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
28504     **/
28505     CImg<T>& diagonal() {
28506       return get_diagonal().move_to(*this);
28507     }
28508 
28509     //! Resize image to become a diagonal matrix \newinstance.
28510     CImg<T> get_diagonal() const {
28511       if (is_empty()) return *this;
28512       const unsigned int siz = (unsigned int)size();
28513       CImg<T> res(siz,siz,1,1,0);
28514       cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off];
28515       return res;
28516     }
28517 
28518     //! Replace the image by an identity matrix.
28519     /**
28520        \note If the instance image is not square, it is resized to a square matrix using its maximum
28521        dimension as a reference.
28522     **/
28523     CImg<T>& identity_matrix() {
28524       return identity_matrix(std::max(_width,_height)).move_to(*this);
28525     }
28526 
28527     //! Replace the image by an identity matrix \newinstance.
28528     CImg<T> get_identity_matrix() const {
28529       return identity_matrix(std::max(_width,_height));
28530     }
28531 
28532     //! Fill image with a linear sequence of values.
28533     /**
28534        \param a0 Starting value of the sequence.
28535        \param a1 Ending value of the sequence.
28536     **/
28537     CImg<T>& sequence(const T& a0, const T& a1) {
28538       if (is_empty()) return *this;
28539       const ulongT siz = size() - 1;
28540       T* ptr = _data;
28541       if (siz) {
28542         const double delta = (double)a1 - (double)a0;
28543         cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
28544       } else *ptr = a0;
28545       return *this;
28546     }
28547 
28548     //! Fill image with a linear sequence of values \newinstance.
28549     CImg<T> get_sequence(const T& a0, const T& a1) const {
28550       return (+*this).sequence(a0,a1);
28551     }
28552 
28553     //! Transpose the image, viewed as a matrix.
28554     /**
28555        \note Equivalent to \code permute_axes("yxzc"); \endcode.
28556     **/
28557     CImg<T>& transpose() {
28558       if (_width==1) { _width = _height; _height = 1; return *this; }
28559       if (_height==1) { _height = _width; _width = 1; return *this; }
28560       if (_width==_height) {
28561         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));
28562         return *this;
28563       }
28564       return get_transpose().move_to(*this);
28565     }
28566 
28567     //! Transpose the image, viewed as a matrix \newinstance.
28568     CImg<T> get_transpose() const {
28569       return get_permute_axes("yxzc");
28570     }
28571 
28572     //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors.
28573     /**
28574        \param img Image used as the second argument of the cross product.
28575        \note The first argument of the cross product is \c *this.
28576      **/
28577     template<typename t>
28578     CImg<T>& cross(const CImg<t>& img) {
28579       if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
28580         throw CImgInstanceException(_cimg_instance
28581                                     "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.",
28582                                     cimg_instance,
28583                                     img._width,img._height,img._depth,img._spectrum,img._data);
28584 
28585       const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
28586       (*this)[0] = (T)(y*img[2] - z*img[1]);
28587       (*this)[1] = (T)(z*img[0] - x*img[2]);
28588       (*this)[2] = (T)(x*img[1] - y*img[0]);
28589       return *this;
28590     }
28591 
28592     //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance.
28593     template<typename t>
28594     CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
28595       return CImg<_cimg_Tt>(*this).cross(img);
28596     }
28597 
28598     //! Invert the instance image, viewed as a matrix.
28599     /**
28600        \param use_LU Choose the inverting algorithm. Can be:
28601        - \c true: LU-based matrix inversion.
28602        - \c false: SVD-based matrix inversion.
28603     **/
28604     CImg<T>& invert(const bool use_LU=true) {
28605       if (_width!=_height || _depth!=1 || _spectrum!=1)
28606         throw CImgInstanceException(_cimg_instance
28607                                     "invert(): Instance is not a square matrix.",
28608                                     cimg_instance);
28609       const double dete = _width>3?-1.:det();
28610       if (dete!=0. && _width==2) {
28611         const double
28612           a = _data[0], c = _data[1],
28613           b = _data[2], d = _data[3];
28614         _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
28615         _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
28616       } else if (dete!=0. && _width==3) {
28617         const double
28618           a = _data[0], d = _data[1], g = _data[2],
28619           b = _data[3], e = _data[4], h = _data[5],
28620           c = _data[6], f = _data[7], i = _data[8];
28621         _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete);
28622         _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete);
28623         _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete);
28624       } else {
28625 
28626 #ifdef cimg_use_lapack
28627         int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
28628         Tfloat
28629           *const lapA = new Tfloat[N*N],
28630           *const WORK = new Tfloat[LWORK];
28631         cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
28632         cimg::getrf(N,lapA,IPIV,INFO);
28633         if (INFO)
28634           cimg::warn(_cimg_instance
28635                      "invert(): LAPACK function dgetrf_() returned error code %d.",
28636                      cimg_instance,
28637                      INFO);
28638         else {
28639           cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
28640           if (INFO)
28641             cimg::warn(_cimg_instance
28642                        "invert(): LAPACK function dgetri_() returned error code %d.",
28643                        cimg_instance,
28644                        INFO);
28645         }
28646         if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0);
28647         delete[] IPIV; delete[] lapA; delete[] WORK;
28648 #else
28649         if (use_LU) { // LU-based
28650           CImg<Tfloat> A(*this,false), indx;
28651           bool d;
28652           A._LU(indx,d);
28653           cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16*16))
28654           cimg_forX(*this,j) {
28655             CImg<Tfloat> col(1,_width,1,1,0);
28656             col(j) = 1;
28657             col._solve(A,indx);
28658             cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
28659           }
28660         } else pseudoinvert(false); // SVD-based
28661 #endif
28662       }
28663       return *this;
28664     }
28665 
28666     //! Invert the instance image, viewed as a matrix \newinstance.
28667     CImg<Tfloat> get_invert(const bool use_LU=true) const {
28668       return CImg<Tfloat>(*this,false).invert(use_LU);
28669     }
28670 
28671     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix.
28672     /**
28673     **/
28674     CImg<T>& pseudoinvert(const bool use_LU=false) {
28675       return get_pseudoinvert(use_LU).move_to(*this);
28676     }
28677 
28678     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance.
28679     CImg<Tfloat> get_pseudoinvert(const bool use_LU=false) const {
28680 
28681       // LU-based method.
28682       if (use_LU) {
28683         CImg<Tfloat> AtA(width(),width());
28684         cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128))
28685         cimg_forY(AtA,i)
28686           for (int j = 0; j<=i; ++j) {
28687             double res = 0;
28688             cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k);
28689             AtA(j,i) = AtA(i,j) = (Tfloat)res;
28690           }
28691         AtA.invert(true);
28692         return AtA*get_transpose();
28693       }
28694 
28695       // SVD-based method.
28696       CImg<Tfloat> U, S, V;
28697       SVD(U,S,V,false);
28698       const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max();
28699       cimg_forX(V,x) {
28700         const Tfloat s = S(x), invs = s>epsilon?1/s:0;
28701         cimg_forY(V,y) V(x,y)*=invs;
28702       }
28703       return V*U.transpose();
28704     }
28705 
28706     //! Solve a system of linear equations.
28707     /**
28708        \param A Matrix of the linear system.
28709        \param use_LU In case of non square system (least-square solution),
28710                      choose between SVD-based (\c false) or LU-based (\c true) method.
28711                      LU method is faster for large matrices, but numerically less stable.
28712        \note Solve \c AX = B where \c B=*this.
28713     **/
28714     template<typename t>
28715     CImg<T>& solve(const CImg<t>& A, const bool use_LU=false) {
28716       if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
28717         throw CImgArgumentException(_cimg_instance
28718                                     "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
28719                                     "incompatible dimensions.",
28720                                     cimg_instance,
28721                                     A._width,A._height,A._depth,A._spectrum,A._data);
28722       typedef _cimg_Ttfloat Ttfloat;
28723 
28724       if (A.size()==1) return (*this)/=A[0];
28725       if (A._width==2 && A._height==2 && _height==2) { // 2x2 linear system
28726         const double a = (double)A[0], b = (double)A[1], c = (double)A[2], d = (double)A[3],
28727           fa = std::fabs(a), fb = std::fabs(b), fc = std::fabs(c), fd = std::fabs(d),
28728           det = a*d - b*c, fM = cimg::max(fa,fb,fc,fd);
28729         if (fM==fa)
28730           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
28731           cimg_forX(*this,k) {
28732             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
28733             (*this)(k,0) = (T)((u - b*y)/a); (*this)(k,1) = (T)y;
28734           } else if (fM==fc)
28735           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
28736           cimg_forX(*this,k) {
28737             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
28738             (*this)(k,0) = (T)((v - d*y)/c); (*this)(k,1) = (T)y;
28739           } else if (fM==fb)
28740           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
28741           cimg_forX(*this,k) {
28742             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
28743             (*this)(k,0) = (T)x; (*this)(k,1) = (T)((u - a*x)/b);
28744           } else
28745           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
28746           cimg_forX(*this,k) {
28747             const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
28748             (*this)(k,0) = (T)x; (*this)(k,1) = (T)((v - c*x)/d);
28749           }
28750         return *this;
28751       }
28752 
28753       if (A._width==A._height) { // Square linear system
28754 #ifdef cimg_use_lapack
28755         char TRANS = 'N';
28756         int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
28757         Ttfloat
28758           *const lapA = new Ttfloat[N*N],
28759           *const lapB = new Ttfloat[N],
28760           *const WORK = new Ttfloat[LWORK];
28761         cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l));
28762         cimg_forX(*this,i) {
28763           cimg_forY(*this,j) lapB[j] = (Ttfloat)((*this)(i,j));
28764           cimg::getrf(N,lapA,IPIV,INFO);
28765           if (INFO)
28766             cimg::warn(_cimg_instance
28767                        "solve(): LAPACK library function dgetrf_() returned error code %d.",
28768                        cimg_instance,
28769                        INFO);
28770           else {
28771             cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
28772             if (INFO)
28773               cimg::warn(_cimg_instance
28774                          "solve(): LAPACK library function dgetrs_() returned error code %d.",
28775                          cimg_instance,
28776                          INFO);
28777           }
28778           if (!INFO) cimg_forY(*this,j) (*this)(i,j) = (T)(lapB[j]); else cimg_forY(*this,j) (*this)(i,j) = (T)0;
28779         }
28780         delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
28781 #else
28782         CImg<Ttfloat> lu(A,false);
28783         CImg<Ttfloat> indx;
28784         bool d;
28785         lu._LU(indx,d);
28786         CImg<T> res(_width,A._width);
28787         cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16))
28788           cimg_forX(*this,i) res.draw_image(i,get_column(i)._solve(lu,indx));
28789         res.move_to(*this);
28790 #endif
28791       } else { // Least-square solution for non-square systems
28792 
28793 #ifdef cimg_use_lapack
28794         char TRANS = 'N';
28795         int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
28796         Ttfloat WORK_QUERY;
28797         Ttfloat
28798           * const lapA = new Ttfloat[M*N],
28799           * const lapB = new Ttfloat[M*NRHS];
28800         cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
28801         LWORK = (int) WORK_QUERY;
28802         Ttfloat *const WORK = new Ttfloat[LWORK];
28803         cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l));
28804         cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l));
28805         cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
28806         if (INFO != 0)
28807           cimg::warn(_cimg_instance
28808                      "solve(): LAPACK library function sgels() returned error code %d.",
28809                      cimg_instance,
28810                      INFO);
28811         assign(NRHS, N);
28812         if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l];
28813         else (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this);
28814         delete[] lapA; delete[] lapB; delete[] WORK;
28815 #else
28816         (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this);
28817 #endif
28818       }
28819       return *this;
28820     }
28821 
28822     //! Solve a system of linear equations \newinstance.
28823     template<typename t>
28824     CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A, const bool use_LU=false) const {
28825       typedef _cimg_Ttfloat Ttfloat;
28826       return CImg<Ttfloat>(*this,false).solve(A,use_LU);
28827     }
28828 
28829     template<typename t, typename ti>
28830     CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
28831       typedef _cimg_Ttfloat Ttfloat;
28832       const int N = height();
28833       int ii = -1;
28834       Ttfloat sum;
28835       for (int i = 0; i<N; ++i) {
28836         const int ip = (int)indx[i];
28837         sum = (*this)(ip);
28838         (*this)(ip) = (*this)(i);
28839         if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j);
28840         else if (sum!=0) ii = i;
28841         (*this)(i) = (T)sum;
28842       }
28843       for (int i = N - 1; i>=0; --i) {
28844         sum = (*this)(i);
28845         for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
28846         (*this)(i) = (T)(sum/A(i,i));
28847       }
28848       return *this;
28849     }
28850 
28851     //! Solve a tridiagonal system of linear equations.
28852     /**
28853        \param A Coefficients of the tridiagonal system.
28854        A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
28855        stored as a 3 columns matrix
28856        \note Solve AX=B where \c B=*this, using the Thomas algorithm.
28857     **/
28858     template<typename t>
28859     CImg<T>& solve_tridiagonal(const CImg<t>& A) {
28860       const unsigned int siz = (unsigned int)size();
28861       if (A._width!=3 || A._height!=siz)
28862         throw CImgArgumentException(_cimg_instance
28863                                     "solve_tridiagonal(): Instance and tridiagonal matrix "
28864                                     "(%u,%u,%u,%u,%p) have incompatible dimensions.",
28865                                     cimg_instance,
28866                                     A._width,A._height,A._depth,A._spectrum,A._data);
28867       typedef _cimg_Ttfloat Ttfloat;
28868       const Ttfloat epsilon = 1e-4f;
28869       CImg<Ttfloat> B = A.get_column(1), V(*this,false);
28870       for (int i = 1; i<(int)siz; ++i) {
28871         const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon);
28872         B[i] -= m*A(2,i - 1);
28873         V[i] -= m*V[i - 1];
28874       }
28875       (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon));
28876       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));
28877       return *this;
28878     }
28879 
28880     //! Solve a tridiagonal system of linear equations \newinstance.
28881     template<typename t>
28882     CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
28883       return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
28884     }
28885 
28886     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
28887     /**
28888        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
28889        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
28890     **/
28891     template<typename t>
28892     const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
28893       if (is_empty()) { val.assign(); vec.assign(); }
28894       else {
28895         if (_width!=_height || _depth>1 || _spectrum>1)
28896           throw CImgInstanceException(_cimg_instance
28897                                       "eigen(): Instance is not a square matrix.",
28898                                       cimg_instance);
28899 
28900         if (val.size()<(ulongT)_width) val.assign(1,_width);
28901         if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width);
28902         switch (_width) {
28903         case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
28904         case 2 : {
28905           const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
28906           double f = e*e - 4*(a*d - b*c);
28907           if (f<0) cimg::warn(_cimg_instance
28908                               "eigen(): Complex eigenvalues found.",
28909                               cimg_instance);
28910           f = std::sqrt(f);
28911           const double
28912             l1 = 0.5*(e - f),
28913             l2 = 0.5*(e + f),
28914             b2 = b*b,
28915             norm1 = std::sqrt(cimg::sqr(l2 - a) + b2),
28916             norm2 = std::sqrt(cimg::sqr(l1 - a) + b2);
28917           val[0] = (t)l2;
28918           val[1] = (t)l1;
28919           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; }
28920           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; }
28921         } break;
28922         default :
28923           throw CImgInstanceException(_cimg_instance
28924                                       "eigen(): Eigenvalues computation of general matrices is limited "
28925                                       "to 2x2 matrices.",
28926                                       cimg_instance);
28927         }
28928       }
28929       return *this;
28930     }
28931 
28932     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
28933     /**
28934        \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
28935     **/
28936     CImgList<Tfloat> get_eigen() const {
28937       CImgList<Tfloat> res(2);
28938       eigen(res[0],res[1]);
28939       return res;
28940     }
28941 
28942     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
28943     /**
28944        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
28945        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
28946     **/
28947     template<typename t>
28948     const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
28949       if (is_empty()) { val.assign(); vec.assign(); return *this; }
28950       if (_width!=_height || _depth>1 || _spectrum>1)
28951         throw CImgInstanceException(_cimg_instance
28952                                     "eigen(): Instance is not a square matrix.",
28953                                     cimg_instance);
28954       val.assign(1,_width);
28955       vec.assign(_width,_width);
28956 
28957       if (_width==1) { val[0] = cimg::abs((*this)[0]); vec[0] = 1; return *this; }
28958       if (_width==2) {
28959         const double
28960           a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3],
28961           e = a + d, f = std::sqrt(std::max(e*e - 4*(a*d - b*c),0.0)),
28962           l1 = 0.5*(e - f), l2 = 0.5*(e + f),
28963           n = std::sqrt(cimg::sqr(l2 - a) + b*b);
28964         val[0] = (t)l2;
28965         val[1] = (t)l1;
28966         if (n>0) { vec[0] = (t)(b/n); vec[2] = (t)((l2 - a)/n); } else { vec[0] = 1; vec[2] = 0; }
28967         vec[1] = -vec[2];
28968         vec[3] = vec[0];
28969         return *this;
28970       }
28971 
28972 #ifdef cimg_use_lapack
28973       char JOB = 'V', UPLO = 'U';
28974       int N = _width, LWORK = 4*N, INFO;
28975       Tfloat
28976         *const lapA = new Tfloat[N*N],
28977         *const lapW = new Tfloat[N],
28978         *const WORK = new Tfloat[LWORK];
28979       cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
28980       cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
28981       if (INFO)
28982         cimg::warn(_cimg_instance
28983                    "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
28984                    cimg_instance,
28985                    INFO);
28986       if (!INFO) {
28987         cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i];
28988         cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]);
28989       } else { val.fill(0); vec.fill(0); }
28990       delete[] lapA; delete[] lapW; delete[] WORK;
28991 
28992 #else
28993       CImg<t> V(_width,_width);
28994       Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M));
28995       (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false);
28996       if (maxabs!=1) val*=maxabs;
28997 
28998       bool is_ambiguous = false;
28999       float eig = 0;
29000       cimg_forY(val,p) { // Check for ambiguous cases
29001         if (val[p]>eig) eig = (float)val[p];
29002         t scal = 0;
29003         cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
29004         if (cimg::abs(scal)<0.9f) is_ambiguous = true;
29005         if (scal<0) val[p] = -val[p];
29006       }
29007       if (is_ambiguous) {
29008         ++(eig*=2);
29009         SVD(vec,val,V,false,40,eig);
29010         val-=eig;
29011       }
29012 
29013       CImg<intT> permutations; // Sort eigenvalues in decreasing order
29014       CImg<t> tmp(_width);
29015       val.sort(permutations,false);
29016       cimg_forY(vec,k) {
29017         cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
29018         std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
29019       }
29020 #endif
29021       return *this;
29022     }
29023 
29024     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
29025     /**
29026        \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in
29027          symmetric_eigen(CImg<t>&,CImg<t>&) const.
29028     **/
29029     CImgList<Tfloat> get_symmetric_eigen() const {
29030       CImgList<Tfloat> res(2);
29031       symmetric_eigen(res[0],res[1]);
29032       return res;
29033     }
29034 
29035     //! Sort pixel values and get sorting permutations.
29036     /**
29037        \param[out] permutations Permutation map used for the sorting.
29038        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
29039     **/
29040     template<typename t>
29041     CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
29042       permutations.assign(_width,_height,_depth,_spectrum);
29043       if (is_empty()) return *this;
29044       cimg_foroff(permutations,off) permutations[off] = (t)off;
29045       return _quicksort(0,size() - 1,permutations,is_increasing,true);
29046     }
29047 
29048     //! Sort pixel values and get sorting permutations \newinstance.
29049     template<typename t>
29050     CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
29051       return (+*this).sort(permutations,is_increasing);
29052     }
29053 
29054     //! Sort pixel values.
29055     /**
29056        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
29057        \param axis Tells if the value sorting must be done along a specific axis. Can be:
29058        - \c 0: All pixel values are sorted, independently on their initial position.
29059        - \c 'x': Image columns are sorted, according to the first value in each column.
29060        - \c 'y': Image rows are sorted, according to the first value in each row.
29061        - \c 'z': Image slices are sorted, according to the first value in each slice.
29062        - \c 'c': Image channels are sorted, according to the first value in each channel.
29063     **/
29064     CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
29065       if (is_empty()) return *this;
29066       CImg<uintT> perm;
29067       switch (cimg::lowercase(axis)) {
29068       case 0 :
29069         _quicksort(0,size() - 1,perm,is_increasing,false);
29070         break;
29071       case 'x' : {
29072         perm.assign(_width);
29073         get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
29074         CImg<T> img(*this,false);
29075         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
29076       } break;
29077       case 'y' : {
29078         perm.assign(_height);
29079         get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
29080         CImg<T> img(*this,false);
29081         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
29082       } break;
29083       case 'z' : {
29084         perm.assign(_depth);
29085         get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
29086         CImg<T> img(*this,false);
29087         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
29088       } break;
29089       case 'c' : {
29090         perm.assign(_spectrum);
29091         get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
29092         CImg<T> img(*this,false);
29093         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
29094       } break;
29095       default :
29096         throw CImgArgumentException(_cimg_instance
29097                                     "sort(): Invalid specified axis '%c' "
29098                                     "(should be { x | y | z | c }).",
29099                                     cimg_instance,axis);
29100       }
29101       return *this;
29102     }
29103 
29104     //! Sort pixel values \newinstance.
29105     CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
29106       return (+*this).sort(is_increasing,axis);
29107     }
29108 
29109     template<typename t>
29110     CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
29111                         const bool is_increasing, const bool is_permutations) {
29112       if (indm<indM) {
29113         const long mid = (indm + indM)/2;
29114         if (is_increasing) {
29115           if ((*this)[indm]>(*this)[mid]) {
29116             cimg::swap((*this)[indm],(*this)[mid]);
29117             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29118           }
29119           if ((*this)[mid]>(*this)[indM]) {
29120             cimg::swap((*this)[indM],(*this)[mid]);
29121             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
29122           }
29123           if ((*this)[indm]>(*this)[mid]) {
29124             cimg::swap((*this)[indm],(*this)[mid]);
29125             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29126           }
29127         } else {
29128           if ((*this)[indm]<(*this)[mid]) {
29129             cimg::swap((*this)[indm],(*this)[mid]);
29130             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29131           }
29132           if ((*this)[mid]<(*this)[indM]) {
29133             cimg::swap((*this)[indM],(*this)[mid]);
29134             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
29135           }
29136           if ((*this)[indm]<(*this)[mid]) {
29137             cimg::swap((*this)[indm],(*this)[mid]);
29138             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
29139           }
29140         }
29141         if (indM - indm>=3) {
29142           const T pivot = (*this)[mid];
29143           long i = indm, j = indM;
29144           if (is_increasing) {
29145             do {
29146               while ((*this)[i]<pivot) ++i;
29147               while ((*this)[j]>pivot) --j;
29148               if (i<=j) {
29149                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
29150                 cimg::swap((*this)[i++],(*this)[j--]);
29151               }
29152             } while (i<=j);
29153           } else {
29154             do {
29155               while ((*this)[i]>pivot) ++i;
29156               while ((*this)[j]<pivot) --j;
29157               if (i<=j) {
29158                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
29159                 cimg::swap((*this)[i++],(*this)[j--]);
29160               }
29161             } while (i<=j);
29162           }
29163           if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
29164           if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
29165         }
29166       }
29167       return *this;
29168     }
29169 
29170     //! Compute the SVD of the instance image, viewed as a general matrix.
29171     /**
29172        Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
29173        and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
29174        \param[out] U First matrix of the SVD product.
29175        \param[out] S Coefficients of the second (diagonal) matrix of the SVD product.
29176          These coefficients are stored as a vector.
29177        \param[out] V Third matrix of the SVD product.
29178        \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
29179        \param max_iteration Maximum number of iterations considered for the algorithm convergence.
29180        \param lambda Epsilon used for the algorithm convergence.
29181        \note The instance matrix can be computed from \c U,\c S and \c V by
29182        \code
29183        const CImg<> A;  // Input matrix (assumed to contain some values)
29184        CImg<> U,S,V;
29185        A.SVD(U,S,V)
29186        \endcode
29187     **/
29188     template<typename t>
29189     const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
29190                        const unsigned int max_iteration=40, const float lambda=0) const {
29191       typedef _cimg_Ttfloat Ttfloat;
29192       const Ttfloat epsilon = (Ttfloat)1e-25;
29193 
29194       if (is_empty()) { U.assign(); S.assign(); V.assign(); }
29195       else if (_depth!=1 || _spectrum!=1)
29196         throw CImgInstanceException(_cimg_instance
29197                                     "SVD(): Instance has invalid dimensions (depth or channels different from 1).",
29198                                     cimg_instance);
29199       else {
29200         U = *this;
29201         if (lambda!=0) {
29202           const unsigned int delta = std::min(U._width,U._height);
29203           for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
29204         }
29205         if (S.size()<_width) S.assign(1,_width);
29206         if (V._width<_width || V._height<_height) V.assign(_width,_width);
29207         CImg<t> rv1(_width);
29208         Ttfloat anorm = 0, c, f, g = 0, h, s, scale = 0;
29209         int l = 0;
29210 
29211         cimg_forX(U,i) {
29212           l = i + 1;
29213           rv1[i] = scale*g;
29214           g = s = scale = 0;
29215           if (i<height()) {
29216             for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k));
29217             if (scale) {
29218               for (int k = i; k<height(); ++k) {
29219                 U(i,k)/=scale;
29220                 s+=U(i,k)*U(i,k);
29221               }
29222               f = U(i,i);
29223               g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
29224               h = f*g - s;
29225               U(i,i) = f - g;
29226               for (int j = l; j<width(); ++j) {
29227                 s = 0;
29228                 for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k);
29229                 f = s/h;
29230                 for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
29231               }
29232               for (int k = i; k<height(); ++k) U(i,k)*=scale;
29233             }
29234           }
29235           S[i] = scale*g;
29236 
29237           g = s = scale = 0;
29238           if (i<height() && i!=width() - 1) {
29239             for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
29240             if (scale) {
29241               for (int k = l; k<width(); ++k) {
29242                 U(k,i)/=scale;
29243                 s+=U(k,i)*U(k,i);
29244               }
29245               f = U(l,i);
29246               g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
29247               h = f*g - s;
29248               U(l,i) = f - g;
29249               for (int k = l; k<width(); ++k) rv1[k] = U(k,i)/h;
29250               for (int j = l; j<height(); ++j) {
29251                 s = 0;
29252                 for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i);
29253                 for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k];
29254               }
29255               for (int k = l; k<width(); ++k) U(k,i)*=scale;
29256             }
29257           }
29258           anorm = (Ttfloat)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i])));
29259         }
29260 
29261         for (int i = width() - 1; i>=0; --i) {
29262           if (i<width() - 1) {
29263             if (g) {
29264               for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
29265               for (int j = l; j<width(); ++j) {
29266                 s = 0;
29267                 for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k);
29268                 for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k);
29269               }
29270             }
29271             for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.;
29272           }
29273           V(i,i) = (t)1;
29274           g = rv1[i];
29275           l = i;
29276         }
29277 
29278         for (int i = std::min(width(),height()) - 1; i>=0; --i) {
29279           l = i + 1;
29280           g = S[i];
29281           for (int j = l; j<width(); ++j) U(j,i) = 0;
29282           if (g) {
29283             g = 1/g;
29284             for (int j = l; j<width(); ++j) {
29285               s = 0;
29286               for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k);
29287               f = (s/U(i,i))*g;
29288               for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
29289             }
29290             for (int j = i; j<height(); ++j) U(i,j)*= g;
29291           } else for (int j = i; j<height(); ++j) U(i,j) = 0;
29292           ++U(i,i);
29293         }
29294 
29295         for (int k = width() - 1; k>=0; --k) {
29296           int nm = 0;
29297           for (unsigned int its = 0; its<max_iteration; ++its) {
29298             bool flag = true;
29299             for (l = k; l>=1; --l) {
29300               nm = l - 1;
29301               if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; }
29302               if ((cimg::abs(S[nm]) + anorm)==anorm) break;
29303             }
29304             if (flag) {
29305               c = 0;
29306               s = 1;
29307               for (int i = l; i<=k; ++i) {
29308                 f = s*rv1[i];
29309                 rv1[i] = c*rv1[i];
29310                 if ((cimg::abs(f) + anorm)==anorm) break;
29311                 g = S[i];
29312                 h = cimg::_hypot(f,g);
29313                 S[i] = h;
29314                 h = 1/h;
29315                 c = g*h;
29316                 s = -f*h;
29317                 cimg_forY(U,j) {
29318                   const t y = U(nm,j), z = U(i,j);
29319                   U(nm,j) = y*c + z*s;
29320                   U(i,j) = z*c - y*s;
29321                 }
29322               }
29323             }
29324 
29325             const t z = S[k];
29326             if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
29327             nm = k - 1;
29328             t x = S[l], y = S[nm];
29329             g = rv1[nm];
29330             h = rv1[k];
29331             f = ((y - z)*(y + z) + (g - h)*(g + h))/std::max(epsilon,(Ttfloat)2*h*y);
29332             g = cimg::_hypot(f,(Ttfloat)1);
29333             f = ((x - z)*(x + z) + h*((y/(f + (f>=0?g:-g))) - h))/std::max(epsilon,(Ttfloat)x);
29334             c = s = 1;
29335             for (int j = l; j<=nm; ++j) {
29336               const int i = j + 1;
29337               g = rv1[i];
29338               h = s*g;
29339               g = c*g;
29340               t y1 = S[i], z1 = cimg::_hypot(f,h);
29341               rv1[j] = z1;
29342               c = f/std::max(epsilon,(Ttfloat)z1);
29343               s = h/std::max(epsilon,(Ttfloat)z1);
29344               f = x*c + g*s;
29345               g = g*c - x*s;
29346               h = y1*s;
29347               y1*=c;
29348               cimg_forX(U,jj) {
29349                 const t x2 = V(j,jj), z2 = V(i,jj);
29350                 V(j,jj) = x2*c + z2*s;
29351                 V(i,jj) = z2*c - x2*s;
29352               }
29353               z1 = cimg::_hypot(f,h);
29354               S[j] = z1;
29355               if (z1) {
29356                 z1 = 1/std::max(epsilon,(Ttfloat)z1);
29357                 c = f*z1;
29358                 s = h*z1;
29359               }
29360               f = c*g + s*y1;
29361               x = c*y1 - s*g;
29362               cimg_forY(U,jj) {
29363                 const t y2 = U(j,jj), z2 = U(i,jj);
29364                 U(j,jj) = y2*c + z2*s;
29365                 U(i,jj) = z2*c - y2*s;
29366               }
29367             }
29368             rv1[l] = 0;
29369             rv1[k] = f;
29370             S[k] = x;
29371           }
29372         }
29373 
29374         if (sorting) {
29375           CImg<intT> permutations;
29376           CImg<t> tmp(_width);
29377           S.sort(permutations,false);
29378           cimg_forY(U,k) {
29379             cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
29380             std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
29381           }
29382           cimg_forY(V,k) {
29383             cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
29384             std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
29385           }
29386         }
29387       }
29388       return *this;
29389     }
29390 
29391     //! Compute the SVD of the instance image, viewed as a general matrix.
29392     /**
29393        \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in
29394          SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
29395     **/
29396     CImgList<Tfloat> get_SVD(const bool sorting=true,
29397                              const unsigned int max_iteration=40, const float lambda=0) const {
29398       CImgList<Tfloat> res(3);
29399       SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
29400       return res;
29401     }
29402 
29403     // [internal] Compute the LU decomposition of a permuted matrix.
29404     template<typename t>
29405     CImg<T>& _LU(CImg<t>& indx, bool& d) {
29406       const int N = width();
29407       int imax = 0;
29408       CImg<Tfloat> vv(N);
29409       indx.assign(N);
29410       d = true;
29411 
29412       bool return0 = false;
29413       cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512))
29414       cimg_forX(*this,i) {
29415         Tfloat vmax = 0;
29416         cimg_forX(*this,j) {
29417           const Tfloat tmp = cimg::abs((*this)(j,i));
29418           if (tmp>vmax) vmax = tmp;
29419         }
29420         if (vmax==0) return0 = true; else vv[i] = 1/vmax;
29421       }
29422       if (return0) { indx.fill(0); return fill(0); }
29423 
29424       cimg_forX(*this,j) {
29425         for (int i = 0; i<j; ++i) {
29426           Tfloat sum = (*this)(j,i);
29427           for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
29428           (*this)(j,i) = (T)sum;
29429         }
29430 
29431         Tfloat vmax = 0;
29432         for (int i = j; i<width(); ++i) {
29433           Tfloat sum = (*this)(j,i);
29434           for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
29435           (*this)(j,i) = (T)sum;
29436           const Tfloat tmp = vv[i]*cimg::abs(sum);
29437           if (tmp>=vmax) { vmax = tmp; imax = i; }
29438         }
29439         if (j!=imax) {
29440           cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
29441           d = !d;
29442           vv[imax] = vv[j];
29443         }
29444         indx[j] = (t)imax;
29445         if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
29446         if (j<N) {
29447           const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
29448           for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
29449         }
29450       }
29451 
29452       return *this;
29453     }
29454 
29455     //! Compute the projection of the instance matrix onto the specified dictionary.
29456     /**
29457        Find the best matching projection of selected matrix onto the span of an over-complete dictionary D,
29458        using the orthogonal projection or (opt. Orthogonal) Matching Pursuit algorithm.
29459        Instance image must a 2D-matrix in which each column represent a signal to project.
29460        \param dictionary A matrix in which each column is an element of the dictionary D.
29461        \param method Tell what projection method is applied. It can be:
29462          - 0 = orthogonal projection (default).
29463          - 1 = matching pursuit.
29464          - 2 = matching pursuit, with a single orthogonal projection step at the end.
29465          - >=3 = orthogonal matching pursuit where an orthogonal projection step is performed
29466                  every 'method-2' iterations.
29467        \param max_iter Sets the max number of iterations processed for each signal.
29468                        If set to '0' (default), 'max_iter' is set to the number of dictionary columns.
29469                        (only meaningful for matching pursuit and its variants).
29470        \param max_residual Gives a stopping criterion on signal reconstruction accuracy.
29471                            (only meaningful for matching pursuit and its variants).
29472        \return A matrix W whose columns correspond to the sparse weights of associated to each input matrix column.
29473                Thus, the matrix product D*W is an approximation of the input matrix.
29474     **/
29475     template<typename t>
29476     CImg<T>& project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
29477                             const unsigned int max_iter=0, const double max_residual=1e-6) {
29478       return get_project_matrix(dictionary,method,max_iter,max_residual).move_to(*this);
29479     }
29480 
29481     template<typename t>
29482     CImg<Tfloat> get_project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
29483                                     const unsigned int max_iter=0, const double max_residual=1e-6) const {
29484       if (_depth!=1 || _spectrum!=1)
29485         throw CImgInstanceException(_cimg_instance
29486                                     "project_matrix(): Instance image is not a matrix.",
29487                                     cimg_instance);
29488       if (dictionary._height!=_height || dictionary._depth!=1 || dictionary._spectrum!=1)
29489         throw CImgArgumentException(_cimg_instance
29490                                     "project_matrix(): Specified dictionary (%u,%u,%u,%u) has an invalid size.",
29491                                     cimg_instance,
29492                                     dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum);
29493 
29494       if (!method) return get_solve(dictionary,true);
29495       CImg<Tfloat> W(_width,dictionary._width,1,1,0);
29496 
29497       // Compute dictionary norm and normalize it.
29498       CImg<Tfloat> D(dictionary,false), Dnorm(D._width);
29499       cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
29500       cimg_forX(Dnorm,d) {
29501         Tfloat norm = 0;
29502         cimg_forY(D,y) norm+=cimg::sqr(D(d,y));
29503         Dnorm[d] = std::max((Tfloat)1e-8,std::sqrt(norm));
29504       }
29505       cimg_forXY(D,d,y) D(d,y)/=Dnorm[d];
29506 
29507       // Matching pursuit.
29508       const unsigned int proj_step = method<3?1:method - 2;
29509       bool is_orthoproj = false;
29510 
29511       cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
29512         cimg_forX(*this,x) {
29513         CImg<Tfloat> S = get_column(x);
29514         const CImg<Tfloat> S0 = method<2?CImg<Tfloat>():S;
29515         Tfloat residual = S.magnitude()/S._height;
29516         const unsigned int nmax = max_iter?max_iter:D._width;
29517 
29518         for (unsigned int n = 0; n<nmax && residual>max_residual; ++n) {
29519 
29520           // Find best matching column in D.
29521           int dmax = 0;
29522           Tfloat absdotmax = 0, dotmax = 0;
29523           cimg_pragma_openmp(parallel for cimg_openmp_if(D._width>=2 && D._width*D._height>=32))
29524           cimg_forX(D,d) {
29525             Tfloat _dot = 0;
29526             cimg_forY(D,y) _dot+=S[y]*D(d,y);
29527             Tfloat absdot = cimg::abs(_dot);
29528             cimg_pragma_openmp(critical(get_project_matrix)) {
29529               if (absdot>absdotmax) {
29530                 absdotmax = absdot;
29531                 dotmax = _dot;
29532                 dmax = d;
29533               }
29534             }
29535           }
29536 
29537           if (!n || method<3 || n%proj_step) {
29538             // Matching Pursuit: Subtract component to signal.
29539             W(x,dmax)+=dotmax;
29540             residual = 0;
29541             cimg_forY(S,y) {
29542               S[y]-=dotmax*D(dmax,y);
29543               residual+=cimg::sqr(S[y]);
29544             }
29545             residual = std::sqrt(residual)/S._height;
29546             is_orthoproj = false;
29547 
29548           } else {
29549             // Orthogonal Matching Pursuit: Orthogonal projection step.
29550             W(x,dmax) = 1; // Used as a marker only.
29551             unsigned int nbW = 0;
29552             cimg_forY(W,d) if (W(x,d)) ++nbW;
29553             CImg<Tfloat> sD(nbW,D._height);
29554             CImg<uintT> inds(nbW);
29555             int sd = 0;
29556             cimg_forY(W,d) if (W(x,d)) {
29557               cimg_forY(sD,y) sD(sd,y) = D(d,y);
29558               inds[sd++] = d;
29559             }
29560             S0.get_solve(sD,true).move_to(sD); // sD is now a one-column vector of weights
29561 
29562             // Recompute residual signal.
29563             S = S0;
29564             cimg_forY(sD,k) {
29565               const Tfloat weight = sD[k];
29566               const unsigned int ind = inds[k];
29567               W(x,ind) = weight;
29568               cimg_forY(S,y) S[y]-=weight*D(ind,y);
29569             }
29570             residual = S.magnitude()/S._height;
29571             is_orthoproj = true;
29572           }
29573         }
29574 
29575         // Perform last orthoprojection step if needed.
29576         if (method>=2 && !is_orthoproj) {
29577           unsigned int nbW = 0;
29578           cimg_forY(W,d) if (W(x,d)) ++nbW;
29579           if (nbW) { // Avoid degenerated case where 0 coefs are used
29580             CImg<Tfloat> sD(nbW,D._height);
29581             CImg<uintT> inds(nbW);
29582             int sd = 0;
29583             cimg_forY(W,d) if (W(x,d)) {
29584               cimg_forY(sD,y) sD(sd,y) = D(d,y);
29585               inds[sd++] = d;
29586             }
29587             S0.get_solve(sD,true).move_to(sD);
29588             cimg_forY(sD,k) W(x,inds[k]) = sD[k];
29589           }
29590         }
29591       }
29592 
29593       // Normalize resulting coefficients according to initial (non-normalized) dictionary.
29594       cimg_forXY(W,x,y) W(x,y)/=Dnorm[y];
29595       return W;
29596     }
29597 
29598     //! Compute minimal path in a graph, using the Dijkstra algorithm.
29599     /**
29600        \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance
29601          between two nodes (i,j).
29602        \param nb_nodes Number of graph nodes.
29603        \param starting_node Index of the starting node.
29604        \param ending_node Index of the ending node (set to ~0U to ignore ending node).
29605        \param previous_node Array that gives the previous node index in the path to the starting node
29606          (optional parameter).
29607        \return Array of distances of each node to the starting node.
29608     **/
29609     template<typename tf, typename t>
29610     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
29611                             const unsigned int starting_node, const unsigned int ending_node,
29612                             CImg<t>& previous_node) {
29613       if (starting_node>=nb_nodes)
29614         throw CImgArgumentException("CImg<%s>::dijkstra(): Specified index of starting node %u is higher "
29615                                     "than number of nodes %u.",
29616                                     pixel_type(),starting_node,nb_nodes);
29617       CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
29618       dist(starting_node) = 0;
29619       previous_node.assign(1,nb_nodes,1,1,(t)-1);
29620       previous_node(starting_node) = (t)starting_node;
29621       CImg<uintT> Q(nb_nodes);
29622       cimg_forX(Q,u) Q(u) = (unsigned int)u;
29623       cimg::swap(Q(starting_node),Q(0));
29624       unsigned int sizeQ = nb_nodes;
29625       while (sizeQ) {
29626         // Update neighbors from minimal vertex
29627         const unsigned int umin = Q(0);
29628         if (umin==ending_node) sizeQ = 0;
29629         else {
29630           const T dmin = dist(umin);
29631           const T infty = cimg::type<T>::max();
29632           for (unsigned int q = 1; q<sizeQ; ++q) {
29633             const unsigned int v = Q(q);
29634             const T d = (T)distance(v,umin);
29635             if (d<infty) {
29636               const T alt = dmin + d;
29637               if (alt<dist(v)) {
29638                 dist(v) = alt;
29639                 previous_node(v) = (t)umin;
29640                 const T distpos = dist(Q(q));
29641                 for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par)
29642                   cimg::swap(Q(pos),Q(par));
29643               }
29644             }
29645           }
29646           // Remove minimal vertex from queue
29647           Q(0) = Q(--sizeQ);
29648           const T distpos = dist(Q(0));
29649           for (unsigned int pos = 0, left = 0, right = 0;
29650                ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) ||
29651                  (right<sizeQ && distpos>dist(Q(right)));) {
29652             if (right<sizeQ) {
29653               if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
29654               else { cimg::swap(Q(pos),Q(right)); pos = right; }
29655             } else { cimg::swap(Q(pos),Q(left)); pos = left; }
29656           }
29657         }
29658       }
29659       return dist;
29660     }
29661 
29662     //! Return minimal path in a graph, using the Dijkstra algorithm.
29663     template<typename tf, typename t>
29664     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
29665                             const unsigned int starting_node, const unsigned int ending_node=~0U) {
29666       CImg<uintT> foo;
29667       return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
29668     }
29669 
29670     //! Return minimal path in a graph, using the Dijkstra algorithm.
29671     /**
29672        \param starting_node Index of the starting node.
29673        \param ending_node Index of the ending node.
29674        \param previous_node Array that gives the previous node index in the path to the starting node
29675          (optional parameter).
29676        \return Array of distances of each node to the starting node.
29677        \note image instance corresponds to the adjacency matrix of the graph.
29678     **/
29679     template<typename t>
29680     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
29681                       CImg<t>& previous_node) {
29682       return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
29683     }
29684 
29685     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
29686     template<typename t>
29687     CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
29688                          CImg<t>& previous_node) const {
29689       if (_width!=_height || _depth!=1 || _spectrum!=1)
29690         throw CImgInstanceException(_cimg_instance
29691                                     "dijkstra(): Instance is not a graph adjacency matrix.",
29692                                     cimg_instance);
29693 
29694       return dijkstra(*this,_width,starting_node,ending_node,previous_node);
29695     }
29696 
29697     //! Return minimal path in a graph, using the Dijkstra algorithm.
29698     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
29699       return get_dijkstra(starting_node,ending_node).move_to(*this);
29700     }
29701 
29702     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
29703     CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
29704       CImg<uintT> foo;
29705       return get_dijkstra(starting_node,ending_node,foo);
29706     }
29707 
29708     //! Return an image containing the character codes of specified string.
29709     /**
29710        \param str input C-string to encode as an image.
29711        \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
29712        \param is_shared Return result that shares its buffer with \p str.
29713     **/
29714     static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
29715       if (!str) return CImg<T>();
29716       return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared);
29717     }
29718 
29719     //! Return a \c 1x1 image containing specified value.
29720     /**
29721        \param a0 First vector value.
29722     **/
29723     static CImg<T> row_vector(const T& a0) {
29724       return vector(a0);
29725     }
29726 
29727     //! Return a \c 2x1 image containing specified values.
29728     /**
29729        \param a0 First vector value.
29730        \param a1 Second vector value.
29731     **/
29732     static CImg<T> row_vector(const T& a0, const T& a1) {
29733       CImg<T> r(2,1);
29734       r[0] = a0; r[1] = a1;
29735       return r;
29736     }
29737 
29738     //! Return a \c 3x1 image containing specified values.
29739     /**
29740        \param a0 First vector value.
29741        \param a1 Second vector value.
29742        \param a2 Third vector value.
29743     **/
29744     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2) {
29745       CImg<T> r(3,1);
29746       r[0] = a0; r[1] = a1; r[2] = a2;
29747       return r;
29748     }
29749 
29750     //! Return a \c 4x1 image containing specified values.
29751     /**
29752        \param a0 First vector value.
29753        \param a1 Second vector value.
29754        \param a2 Third vector value.
29755        \param a3 Fourth vector value.
29756     **/
29757     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3) {
29758       CImg<T> r(4,1);
29759       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
29760       return r;
29761     }
29762 
29763     //! Return a \c 5x1 image containing specified values.
29764     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
29765       CImg<T> r(5,1);
29766       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
29767       return r;
29768     }
29769 
29770     //! Return a \c 6x1 image containing specified values.
29771     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
29772       CImg<T> r(6,1);
29773       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
29774       return r;
29775     }
29776 
29777     //! Return a \c 7x1 image containing specified values.
29778     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29779                               const T& a4, const T& a5, const T& a6) {
29780       CImg<T> r(7,1);
29781       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
29782       return r;
29783     }
29784 
29785     //! Return a \c 8x1 image containing specified values.
29786     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29787                               const T& a4, const T& a5, const T& a6, const T& a7) {
29788       CImg<T> r(8,1);
29789       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
29790       return r;
29791     }
29792 
29793     //! Return a \c 9x1 image containing specified values.
29794     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29795                               const T& a4, const T& a5, const T& a6, const T& a7,
29796                               const T& a8) {
29797       CImg<T> r(9,1);
29798       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;
29799       return r;
29800     }
29801 
29802     //! Return a \c 10x1 image containing specified values.
29803     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29804                               const T& a4, const T& a5, const T& a6, const T& a7,
29805                               const T& a8, const T& a9) {
29806       CImg<T> r(10,1);
29807       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;
29808       return r;
29809     }
29810 
29811     //! Return a \c 11x1 image containing specified values.
29812     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29813                               const T& a4, const T& a5, const T& a6, const T& a7,
29814                               const T& a8, const T& a9, const T& a10) {
29815       CImg<T> r(11,1);
29816       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;
29817       r[10] = a10;
29818       return r;
29819     }
29820 
29821     //! Return a \c 12x1 image containing specified values.
29822     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29823                               const T& a4, const T& a5, const T& a6, const T& a7,
29824                               const T& a8, const T& a9, const T& a10, const T& a11) {
29825       CImg<T> r(12,1);
29826       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;
29827       r[10] = a10; r[11] = a11;
29828       return r;
29829     }
29830 
29831     //! Return a \c 13x1 image containing specified values.
29832     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29833                               const T& a4, const T& a5, const T& a6, const T& a7,
29834                               const T& a8, const T& a9, const T& a10, const T& a11,
29835                               const T& a12) {
29836       CImg<T> r(13,1);
29837       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;
29838       r[10] = a10; r[11] = a11; r[12] = a12;
29839       return r;
29840     }
29841 
29842     //! Return a \c 14x1 image containing specified values.
29843     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29844                               const T& a4, const T& a5, const T& a6, const T& a7,
29845                               const T& a8, const T& a9, const T& a10, const T& a11,
29846                               const T& a12, const T& a13) {
29847       CImg<T> r(14,1);
29848       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;
29849       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
29850       return r;
29851     }
29852 
29853     //! Return a \c 15x1 image containing specified values.
29854     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29855                               const T& a4, const T& a5, const T& a6, const T& a7,
29856                               const T& a8, const T& a9, const T& a10, const T& a11,
29857                               const T& a12, const T& a13, const T& a14) {
29858       CImg<T> r(15,1);
29859       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;
29860       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
29861       return r;
29862     }
29863 
29864     //! Return a \c 16x1 image containing specified values.
29865     static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
29866                               const T& a4, const T& a5, const T& a6, const T& a7,
29867                               const T& a8, const T& a9, const T& a10, const T& a11,
29868                               const T& a12, const T& a13, const T& a14, const T& a15) {
29869       CImg<T> r(16,1);
29870       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;
29871       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
29872       return r;
29873     }
29874 
29875     //! Return a \c 1x1 image containing specified value.
29876     /**
29877        \param a0 First vector value.
29878     **/
29879     static CImg<T> vector(const T& a0) {
29880       CImg<T> r(1,1);
29881       r[0] = a0;
29882       return r;
29883     }
29884 
29885     //! Return a \c 1x2 image containing specified values.
29886     /**
29887        \param a0 First vector value.
29888        \param a1 Second vector value.
29889     **/
29890     static CImg<T> vector(const T& a0, const T& a1) {
29891       CImg<T> r(1,2);
29892       r[0] = a0; r[1] = a1;
29893       return r;
29894     }
29895 
29896     //! Return a \c 1x3 image containing specified values.
29897     /**
29898        \param a0 First vector value.
29899        \param a1 Second vector value.
29900        \param a2 Third vector value.
29901     **/
29902     static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
29903       CImg<T> r(1,3);
29904       r[0] = a0; r[1] = a1; r[2] = a2;
29905       return r;
29906     }
29907 
29908     //! Return a \c 1x4 image containing specified values.
29909     /**
29910        \param a0 First vector value.
29911        \param a1 Second vector value.
29912        \param a2 Third vector value.
29913        \param a3 Fourth vector value.
29914     **/
29915     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
29916       CImg<T> r(1,4);
29917       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
29918       return r;
29919     }
29920 
29921     //! Return a \c 1x5 image containing specified values.
29922     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
29923       CImg<T> r(1,5);
29924       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
29925       return r;
29926     }
29927 
29928     //! Return a \c 1x6 image containing specified values.
29929     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
29930       CImg<T> r(1,6);
29931       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
29932       return r;
29933     }
29934 
29935     //! Return a \c 1x7 image containing specified values.
29936     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
29937                           const T& a4, const T& a5, const T& a6) {
29938       CImg<T> r(1,7);
29939       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
29940       return r;
29941     }
29942 
29943     //! Return a \c 1x8 image containing specified values.
29944     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
29945                           const T& a4, const T& a5, const T& a6, const T& a7) {
29946       CImg<T> r(1,8);
29947       r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
29948       return r;
29949     }
29950 
29951     //! Return a \c 1x9 image containing specified values.
29952     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
29953                           const T& a4, const T& a5, const T& a6, const T& a7,
29954                           const T& a8) {
29955       CImg<T> r(1,9);
29956       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;
29957       return r;
29958     }
29959 
29960     //! Return a \c 1x10 image containing specified values.
29961     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
29962                           const T& a4, const T& a5, const T& a6, const T& a7,
29963                           const T& a8, const T& a9) {
29964       CImg<T> r(1,10);
29965       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;
29966       return r;
29967     }
29968 
29969     //! Return a \c 1x11 image containing specified values.
29970     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
29971                           const T& a4, const T& a5, const T& a6, const T& a7,
29972                           const T& a8, const T& a9, const T& a10) {
29973       CImg<T> r(1,11);
29974       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;
29975       r[10] = a10;
29976       return r;
29977     }
29978 
29979     //! Return a \c 1x12 image containing specified values.
29980     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
29981                           const T& a4, const T& a5, const T& a6, const T& a7,
29982                           const T& a8, const T& a9, const T& a10, const T& a11) {
29983       CImg<T> r(1,12);
29984       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;
29985       r[10] = a10; r[11] = a11;
29986       return r;
29987     }
29988 
29989     //! Return a \c 1x13 image containing specified values.
29990     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
29991                           const T& a4, const T& a5, const T& a6, const T& a7,
29992                           const T& a8, const T& a9, const T& a10, const T& a11,
29993                           const T& a12) {
29994       CImg<T> r(1,13);
29995       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;
29996       r[10] = a10; r[11] = a11; r[12] = a12;
29997       return r;
29998     }
29999 
30000     //! Return a \c 1x14 image containing specified values.
30001     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30002                           const T& a4, const T& a5, const T& a6, const T& a7,
30003                           const T& a8, const T& a9, const T& a10, const T& a11,
30004                           const T& a12, const T& a13) {
30005       CImg<T> r(1,14);
30006       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;
30007       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
30008       return r;
30009     }
30010 
30011     //! Return a \c 1x15 image containing specified values.
30012     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30013                           const T& a4, const T& a5, const T& a6, const T& a7,
30014                           const T& a8, const T& a9, const T& a10, const T& a11,
30015                           const T& a12, const T& a13, const T& a14) {
30016       CImg<T> r(1,15);
30017       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;
30018       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
30019       return r;
30020     }
30021 
30022     //! Return a \c 1x16 image containing specified values.
30023     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
30024                           const T& a4, const T& a5, const T& a6, const T& a7,
30025                           const T& a8, const T& a9, const T& a10, const T& a11,
30026                           const T& a12, const T& a13, const T& a14, const T& a15) {
30027       CImg<T> r(1,16);
30028       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;
30029       r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
30030       return r;
30031     }
30032 
30033     //! Return a 1x1 matrix containing specified coefficients.
30034     /**
30035        \param a0 First matrix value.
30036        \note Equivalent to vector(const T&).
30037     **/
30038     static CImg<T> matrix(const T& a0) {
30039       return vector(a0);
30040     }
30041 
30042     //! Return a 2x2 matrix containing specified coefficients.
30043     /**
30044        \param a0 First matrix value.
30045        \param a1 Second matrix value.
30046        \param a2 Third matrix value.
30047        \param a3 Fourth matrix value.
30048     **/
30049     static CImg<T> matrix(const T& a0, const T& a1,
30050                           const T& a2, const T& a3) {
30051       CImg<T> r(2,2); T *ptr = r._data;
30052       *(ptr++) = a0; *(ptr++) = a1;
30053       *(ptr++) = a2; *(ptr++) = a3;
30054       return r;
30055     }
30056 
30057     //! Return a 3x3 matrix containing specified coefficients.
30058     /**
30059        \param a0 First matrix value.
30060        \param a1 Second matrix value.
30061        \param a2 Third matrix value.
30062        \param a3 Fourth matrix value.
30063        \param a4 Fifth matrix value.
30064        \param a5 Sixth matrix value.
30065        \param a6 Seventh matrix value.
30066        \param a7 Eighth matrix value.
30067        \param a8 Ninth matrix value.
30068     **/
30069     static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
30070                           const T& a3, const T& a4, const T& a5,
30071                           const T& a6, const T& a7, const T& a8) {
30072       CImg<T> r(3,3); T *ptr = r._data;
30073       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
30074       *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
30075       *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
30076       return r;
30077     }
30078 
30079     //! Return a 4x4 matrix containing specified coefficients.
30080     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
30081                           const T& a4, const T& a5, const T& a6, const T& a7,
30082                           const T& a8, const T& a9, const T& a10, const T& a11,
30083                           const T& a12, const T& a13, const T& a14, const T& a15) {
30084       CImg<T> r(4,4); T *ptr = r._data;
30085       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
30086       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
30087       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
30088       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
30089       return r;
30090     }
30091 
30092     //! Return a 5x5 matrix containing specified coefficients.
30093     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
30094                           const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
30095                           const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
30096                           const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
30097                           const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
30098       CImg<T> r(5,5); T *ptr = r._data;
30099       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
30100       *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
30101       *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
30102       *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
30103       *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
30104       return r;
30105     }
30106 
30107     //! Return a 1x1 symmetric matrix containing specified coefficients.
30108     /**
30109        \param a0 First matrix value.
30110        \note Equivalent to vector(const T&).
30111     **/
30112     static CImg<T> tensor(const T& a0) {
30113       return matrix(a0);
30114     }
30115 
30116     //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
30117     static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
30118       return matrix(a0,a1,a1,a2);
30119     }
30120 
30121     //! Return a 3x3 symmetric matrix containing specified coefficients.
30122     static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
30123       return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
30124     }
30125 
30126     //! Return a 1x1 diagonal matrix containing specified coefficients.
30127     static CImg<T> diagonal(const T& a0) {
30128       return matrix(a0);
30129     }
30130 
30131     //! Return a 2x2 diagonal matrix containing specified coefficients.
30132     static CImg<T> diagonal(const T& a0, const T& a1) {
30133       return matrix(a0,0,0,a1);
30134     }
30135 
30136     //! Return a 3x3 diagonal matrix containing specified coefficients.
30137     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
30138       return matrix(a0,0,0,0,a1,0,0,0,a2);
30139     }
30140 
30141     //! Return a 4x4 diagonal matrix containing specified coefficients.
30142     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
30143       return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
30144     }
30145 
30146     //! Return a 5x5 diagonal matrix containing specified coefficients.
30147     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
30148       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);
30149     }
30150 
30151     //! Return a NxN identity matrix.
30152     /**
30153        \param N Dimension of the matrix.
30154     **/
30155     static CImg<T> identity_matrix(const unsigned int N) {
30156       CImg<T> res(N,N,1,1,0);
30157       cimg_forX(res,x) res(x,x) = 1;
30158       return res;
30159     }
30160 
30161     //! Return a N-numbered sequence vector from \p a0 to \p a1.
30162     /**
30163        \param N Size of the resulting vector.
30164        \param a0 Starting value of the sequence.
30165        \param a1 Ending value of the sequence.
30166      **/
30167     static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) {
30168       if (N) return CImg<T>(1,N).sequence(a0,a1);
30169       return CImg<T>();
30170     }
30171 
30172     //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion.
30173     /**
30174        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
30175        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
30176        \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
30177        \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
30178        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
30179      **/
30180     static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
30181                                    const bool is_quaternion=false) {
30182       double X, Y, Z, W, N;
30183       if (is_quaternion) {
30184         N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w);
30185         if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; }
30186         else { X = Y = Z = 0; W = 1; }
30187         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),
30188                                (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y),
30189                                (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W));
30190       }
30191       N = cimg::hypot((double)x,(double)y,(double)z);
30192       if (N>0) { X = x/N; Y = y/N; Z = z/N; }
30193       else { X = Y = 0; Z = 1; }
30194       const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang);
30195       return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s),
30196                              (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s),
30197                              (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c));
30198     }
30199 
30200     //@}
30201     //-----------------------------------
30202     //
30203     //! \name Value Manipulation
30204     //@{
30205     //-----------------------------------
30206 
30207     //! Fill all pixel values with specified value.
30208     /**
30209        \param val Fill value.
30210     **/
30211     CImg<T>& fill(const T& val) {
30212       if (is_empty()) return *this;
30213       if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
30214       else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*)
30215       return *this;
30216     }
30217 
30218     //! Fill all pixel values with specified value \newinstance.
30219     CImg<T> get_fill(const T& val) const {
30220       return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
30221     }
30222 
30223     //! Fill sequentially all pixel values with specified values.
30224     /**
30225        \param val0 First fill value.
30226        \param val1 Second fill value.
30227     **/
30228     CImg<T>& fill(const T& val0, const T& val1) {
30229       if (is_empty()) return *this;
30230       T *ptrd, *ptre = end() - 1;
30231       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
30232       if (ptrd!=ptre + 1) *(ptrd++) = val0;
30233       return *this;
30234     }
30235 
30236     //! Fill sequentially all pixel values with specified values \newinstance.
30237     CImg<T> get_fill(const T& val0, const T& val1) const {
30238       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
30239     }
30240 
30241     //! Fill sequentially all pixel values with specified values \overloading.
30242     CImg<T>& fill(const T& val0, const T& val1, const T& val2) {
30243       if (is_empty()) return *this;
30244       T *ptrd, *ptre = end() - 2;
30245       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
30246       ptre+=2;
30247       switch (ptre - ptrd) {
30248       case 2 : *(--ptre) = val1; // fallthrough
30249       case 1 : *(--ptre) = val0; // fallthrough
30250       }
30251       return *this;
30252     }
30253 
30254     //! Fill sequentially all pixel values with specified values \newinstance.
30255     CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const {
30256       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
30257     }
30258 
30259     //! Fill sequentially all pixel values with specified values \overloading.
30260     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) {
30261       if (is_empty()) return *this;
30262       T *ptrd, *ptre = end() - 3;
30263       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
30264       ptre+=3;
30265       switch (ptre - ptrd) {
30266       case 3 : *(--ptre) = val2; // fallthrough
30267       case 2 : *(--ptre) = val1; // fallthrough
30268       case 1 : *(--ptre) = val0; // fallthrough
30269       }
30270       return *this;
30271     }
30272 
30273     //! Fill sequentially all pixel values with specified values \newinstance.
30274     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const {
30275       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
30276     }
30277 
30278     //! Fill sequentially all pixel values with specified values \overloading.
30279     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) {
30280       if (is_empty()) return *this;
30281       T *ptrd, *ptre = end() - 4;
30282       for (ptrd = _data; ptrd<ptre; ) {
30283         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
30284       }
30285       ptre+=4;
30286       switch (ptre - ptrd) {
30287       case 4 : *(--ptre) = val3; // fallthrough
30288       case 3 : *(--ptre) = val2; // fallthrough
30289       case 2 : *(--ptre) = val1; // fallthrough
30290       case 1 : *(--ptre) = val0; // fallthrough
30291       }
30292       return *this;
30293     }
30294 
30295     //! Fill sequentially all pixel values with specified values \newinstance.
30296     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const {
30297       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
30298     }
30299 
30300     //! Fill sequentially all pixel values with specified values \overloading.
30301     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) {
30302       if (is_empty()) return *this;
30303       T *ptrd, *ptre = end() - 5;
30304       for (ptrd = _data; ptrd<ptre; ) {
30305         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30306       }
30307       ptre+=5;
30308       switch (ptre - ptrd) {
30309       case 5 : *(--ptre) = val4; // fallthrough
30310       case 4 : *(--ptre) = val3; // fallthrough
30311       case 3 : *(--ptre) = val2; // fallthrough
30312       case 2 : *(--ptre) = val1; // fallthrough
30313       case 1 : *(--ptre) = val0; // fallthrough
30314       }
30315       return *this;
30316     }
30317 
30318     //! Fill sequentially all pixel values with specified values \newinstance.
30319     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
30320       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
30321     }
30322 
30323     //! Fill sequentially all pixel values with specified values \overloading.
30324     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30325                   const T& val6) {
30326       if (is_empty()) return *this;
30327       T *ptrd, *ptre = end() - 6;
30328       for (ptrd = _data; ptrd<ptre; ) {
30329         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30330         *(ptrd++) = val6;
30331       }
30332       ptre+=6;
30333       switch (ptre - ptrd) {
30334       case 6 : *(--ptre) = val5; // fallthrough
30335       case 5 : *(--ptre) = val4; // fallthrough
30336       case 4 : *(--ptre) = val3; // fallthrough
30337       case 3 : *(--ptre) = val2; // fallthrough
30338       case 2 : *(--ptre) = val1; // fallthrough
30339       case 1 : *(--ptre) = val0; // fallthrough
30340       }
30341       return *this;
30342     }
30343 
30344     //! Fill sequentially all pixel values with specified values \newinstance.
30345     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30346                      const T& val6) const {
30347       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
30348     }
30349 
30350     //! Fill sequentially all pixel values with specified values \overloading.
30351     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30352                   const T& val6, const T& val7) {
30353       if (is_empty()) return *this;
30354       T *ptrd, *ptre = end() - 7;
30355       for (ptrd = _data; ptrd<ptre; ) {
30356         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
30357         *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
30358       }
30359       ptre+=7;
30360       switch (ptre - ptrd) {
30361       case 7 : *(--ptre) = val6; // fallthrough
30362       case 6 : *(--ptre) = val5; // fallthrough
30363       case 5 : *(--ptre) = val4; // fallthrough
30364       case 4 : *(--ptre) = val3; // fallthrough
30365       case 3 : *(--ptre) = val2; // fallthrough
30366       case 2 : *(--ptre) = val1; // fallthrough
30367       case 1 : *(--ptre) = val0; // fallthrough
30368       }
30369       return *this;
30370     }
30371 
30372     //! Fill sequentially all pixel values with specified values \newinstance.
30373     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30374                      const T& val6, const T& val7) const {
30375       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
30376     }
30377 
30378     //! Fill sequentially all pixel values with specified values \overloading.
30379     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30380                   const T& val6, const T& val7, const T& val8) {
30381       if (is_empty()) return *this;
30382       T *ptrd, *ptre = end() - 8;
30383       for (ptrd = _data; ptrd<ptre; ) {
30384         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
30385         *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30386         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
30387       }
30388       ptre+=8;
30389       switch (ptre - ptrd) {
30390       case 8 : *(--ptre) = val7; // fallthrough
30391       case 7 : *(--ptre) = val6; // fallthrough
30392       case 6 : *(--ptre) = val5; // fallthrough
30393       case 5 : *(--ptre) = val4; // fallthrough
30394       case 4 : *(--ptre) = val3; // fallthrough
30395       case 3 : *(--ptre) = val2; // fallthrough
30396       case 2 : *(--ptre) = val1; // fallthrough
30397       case 1 : *(--ptre) = val0; // fallthrough
30398       }
30399       return *this;
30400     }
30401 
30402     //! Fill sequentially all pixel values with specified values \newinstance.
30403     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30404                      const T& val6, const T& val7, const T& val8) const {
30405       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
30406     }
30407 
30408     //! Fill sequentially all pixel values with specified values \overloading.
30409     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30410                   const T& val6, const T& val7, const T& val8, const T& val9) {
30411       if (is_empty()) return *this;
30412       T *ptrd, *ptre = end() - 9;
30413       for (ptrd = _data; ptrd<ptre; ) {
30414         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
30415         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
30416       }
30417       ptre+=9;
30418       switch (ptre - ptrd) {
30419       case 9 : *(--ptre) = val8; // fallthrough
30420       case 8 : *(--ptre) = val7; // fallthrough
30421       case 7 : *(--ptre) = val6; // fallthrough
30422       case 6 : *(--ptre) = val5; // fallthrough
30423       case 5 : *(--ptre) = val4; // fallthrough
30424       case 4 : *(--ptre) = val3; // fallthrough
30425       case 3 : *(--ptre) = val2; // fallthrough
30426       case 2 : *(--ptre) = val1; // fallthrough
30427       case 1 : *(--ptre) = val0; // fallthrough
30428       }
30429       return *this;
30430     }
30431 
30432     //! Fill sequentially all pixel values with specified values \newinstance.
30433     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30434                      const T& val6, const T& val7, const T& val8, const T& val9) const {
30435       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
30436     }
30437 
30438     //! Fill sequentially all pixel values with specified values \overloading.
30439     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30440                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) {
30441       if (is_empty()) return *this;
30442       T *ptrd, *ptre = end() - 10;
30443       for (ptrd = _data; ptrd<ptre; ) {
30444         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
30445         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
30446         *(ptrd++) = val10;
30447       }
30448       ptre+=10;
30449       switch (ptre - ptrd) {
30450       case 10 : *(--ptre) = val9; // fallthrough
30451       case 9 : *(--ptre) = val8; // fallthrough
30452       case 8 : *(--ptre) = val7; // fallthrough
30453       case 7 : *(--ptre) = val6; // fallthrough
30454       case 6 : *(--ptre) = val5; // fallthrough
30455       case 5 : *(--ptre) = val4; // fallthrough
30456       case 4 : *(--ptre) = val3; // fallthrough
30457       case 3 : *(--ptre) = val2; // fallthrough
30458       case 2 : *(--ptre) = val1; // fallthrough
30459       case 1 : *(--ptre) = val0; // fallthrough
30460       }
30461       return *this;
30462     }
30463 
30464     //! Fill sequentially all pixel values with specified values \newinstance.
30465     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30466                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const {
30467       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
30468     }
30469 
30470     //! Fill sequentially all pixel values with specified values \overloading.
30471     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30472                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) {
30473       if (is_empty()) return *this;
30474       T *ptrd, *ptre = end() - 11;
30475       for (ptrd = _data; ptrd<ptre; ) {
30476         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30477         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30478       }
30479       ptre+=11;
30480       switch (ptre - ptrd) {
30481       case 11 : *(--ptre) = val10; // fallthrough
30482       case 10 : *(--ptre) = val9; // fallthrough
30483       case 9 : *(--ptre) = val8; // fallthrough
30484       case 8 : *(--ptre) = val7; // fallthrough
30485       case 7 : *(--ptre) = val6; // fallthrough
30486       case 6 : *(--ptre) = val5; // fallthrough
30487       case 5 : *(--ptre) = val4; // fallthrough
30488       case 4 : *(--ptre) = val3; // fallthrough
30489       case 3 : *(--ptre) = val2; // fallthrough
30490       case 2 : *(--ptre) = val1; // fallthrough
30491       case 1 : *(--ptre) = val0; // fallthrough
30492       }
30493       return *this;
30494     }
30495 
30496     //! Fill sequentially all pixel values with specified values \newinstance.
30497     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30498                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const {
30499       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30500                                                            val11);
30501     }
30502 
30503     //! Fill sequentially all pixel values with specified values \overloading.
30504     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30505                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30506                   const T& val12) {
30507       if (is_empty()) return *this;
30508       T *ptrd, *ptre = end() - 12;
30509       for (ptrd = _data; ptrd<ptre; ) {
30510         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30511         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30512         *(ptrd++) = val12;
30513       }
30514       ptre+=12;
30515       switch (ptre - ptrd) {
30516       case 12 : *(--ptre) = val11; // fallthrough
30517       case 11 : *(--ptre) = val10; // fallthrough
30518       case 10 : *(--ptre) = val9; // fallthrough
30519       case 9 : *(--ptre) = val8; // fallthrough
30520       case 8 : *(--ptre) = val7; // fallthrough
30521       case 7 : *(--ptre) = val6; // fallthrough
30522       case 6 : *(--ptre) = val5; // fallthrough
30523       case 5 : *(--ptre) = val4; // fallthrough
30524       case 4 : *(--ptre) = val3; // fallthrough
30525       case 3 : *(--ptre) = val2; // fallthrough
30526       case 2 : *(--ptre) = val1; // fallthrough
30527       case 1 : *(--ptre) = val0; // fallthrough
30528       }
30529       return *this;
30530     }
30531 
30532     //! Fill sequentially all pixel values with specified values \newinstance.
30533     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30534                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30535                      const T& val12) const {
30536       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30537                                                            val11,val12);
30538     }
30539 
30540     //! Fill sequentially all pixel values with specified values \overloading.
30541     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30542                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30543                   const T& val12, const T& val13) {
30544       if (is_empty()) return *this;
30545       T *ptrd, *ptre = end() - 13;
30546       for (ptrd = _data; ptrd<ptre; ) {
30547         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30548         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30549         *(ptrd++) = val12; *(ptrd++) = val13;
30550       }
30551       ptre+=13;
30552       switch (ptre - ptrd) {
30553       case 13 : *(--ptre) = val12; // fallthrough
30554       case 12 : *(--ptre) = val11; // fallthrough
30555       case 11 : *(--ptre) = val10; // fallthrough
30556       case 10 : *(--ptre) = val9; // fallthrough
30557       case 9 : *(--ptre) = val8; // fallthrough
30558       case 8 : *(--ptre) = val7; // fallthrough
30559       case 7 : *(--ptre) = val6; // fallthrough
30560       case 6 : *(--ptre) = val5; // fallthrough
30561       case 5 : *(--ptre) = val4; // fallthrough
30562       case 4 : *(--ptre) = val3; // fallthrough
30563       case 3 : *(--ptre) = val2; // fallthrough
30564       case 2 : *(--ptre) = val1; // fallthrough
30565       case 1 : *(--ptre) = val0; // fallthrough
30566       }
30567       return *this;
30568     }
30569 
30570     //! Fill sequentially all pixel values with specified values \newinstance.
30571     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30572                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30573                      const T& val12, const T& val13) const {
30574       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30575                                                            val11,val12,val13);
30576     }
30577 
30578     //! Fill sequentially all pixel values with specified values \overloading.
30579     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30580                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30581                   const T& val12, const T& val13, const T& val14) {
30582       if (is_empty()) return *this;
30583       T *ptrd, *ptre = end() - 14;
30584       for (ptrd = _data; ptrd<ptre; ) {
30585         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30586         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30587         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
30588       }
30589       ptre+=14;
30590       switch (ptre - ptrd) {
30591       case 14 : *(--ptre) = val13; // fallthrough
30592       case 13 : *(--ptre) = val12; // fallthrough
30593       case 12 : *(--ptre) = val11; // fallthrough
30594       case 11 : *(--ptre) = val10; // fallthrough
30595       case 10 : *(--ptre) = val9; // fallthrough
30596       case 9 : *(--ptre) = val8; // fallthrough
30597       case 8 : *(--ptre) = val7; // fallthrough
30598       case 7 : *(--ptre) = val6; // fallthrough
30599       case 6 : *(--ptre) = val5; // fallthrough
30600       case 5 : *(--ptre) = val4; // fallthrough
30601       case 4 : *(--ptre) = val3; // fallthrough
30602       case 3 : *(--ptre) = val2; // fallthrough
30603       case 2 : *(--ptre) = val1; // fallthrough
30604       case 1 : *(--ptre) = val0; // fallthrough
30605       }
30606       return *this;
30607     }
30608 
30609     //! Fill sequentially all pixel values with specified values \newinstance.
30610     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30611                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30612                      const T& val12, const T& val13, const T& val14) const {
30613       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30614                                                            val11,val12,val13,val14);
30615     }
30616 
30617     //! Fill sequentially all pixel values with specified values \overloading.
30618     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30619                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30620                   const T& val12, const T& val13, const T& val14, const T& val15) {
30621       if (is_empty()) return *this;
30622       T *ptrd, *ptre = end() - 15;
30623       for (ptrd = _data; ptrd<ptre; ) {
30624         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
30625         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
30626         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
30627       }
30628       ptre+=15;
30629       switch (ptre - ptrd) {
30630       case 15 : *(--ptre) = val14; // fallthrough
30631       case 14 : *(--ptre) = val13; // fallthrough
30632       case 13 : *(--ptre) = val12; // fallthrough
30633       case 12 : *(--ptre) = val11; // fallthrough
30634       case 11 : *(--ptre) = val10; // fallthrough
30635       case 10 : *(--ptre) = val9; // fallthrough
30636       case 9 : *(--ptre) = val8; // fallthrough
30637       case 8 : *(--ptre) = val7; // fallthrough
30638       case 7 : *(--ptre) = val6; // fallthrough
30639       case 6 : *(--ptre) = val5; // fallthrough
30640       case 5 : *(--ptre) = val4; // fallthrough
30641       case 4 : *(--ptre) = val3; // fallthrough
30642       case 3 : *(--ptre) = val2; // fallthrough
30643       case 2 : *(--ptre) = val1; // fallthrough
30644       case 1 : *(--ptre) = val0; // fallthrough
30645       }
30646       return *this;
30647     }
30648 
30649     //! Fill sequentially all pixel values with specified values \newinstance.
30650     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
30651                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
30652                      const T& val12, const T& val13, const T& val14, const T& val15) const {
30653       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
30654                                                            val11,val12,val13,val14,val15);
30655     }
30656 
30657     //! Fill sequentially pixel values according to a given expression.
30658     /**
30659        \param expression C-string describing a math formula, or a sequence of values.
30660        \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling.
30661        \param allow_formula Tells that mathematical formulas are authorized for the filling.
30662        \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression.
30663        \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression.
30664     **/
30665     CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
30666                   const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
30667       return _fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs,"fill",0);
30668     }
30669 
30670     // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula |
30671     //                    2 = allow formula and do not fill image values }.
30672     CImg<T>& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode,
30673                    const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs,
30674                    const char *const calling_function, const CImg<T> *provides_copy) {
30675       if (is_empty() || !expression || !*expression) return *this;
30676       const unsigned int omode = cimg::exception_mode();
30677       cimg::exception_mode(0);
30678       CImg<charT> is_error;
30679       bool is_value_sequence = false;
30680       cimg_abort_init;
30681 
30682       if (formula_mode) {
30683 
30684         // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser.
30685         double value;
30686         char sep;
30687         const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
30688         if (err==1 || (err==2 && sep==',')) {
30689           if (err==1) { if (formula_mode==2) return *this; return fill((T)value); }
30690           else is_value_sequence = true;
30691         }
30692 
30693         // Try to fill values according to a formula.
30694         _cimg_abort_init_openmp;
30695         if (!is_value_sequence) try {
30696             CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
30697             _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
30698                                                *expression=='*' || *expression==':'),
30699                                  calling_function,base,this,list_inputs,list_outputs,true);
30700             if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' &&
30701                 mp.need_input_copy)
30702               base.assign().assign(*this,false); // Needs input copy
30703 
30704             // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation).
30705             unsigned int M;
30706             if (mp.result_dim) {
30707               M = cimg::max(_width,_height,_depth);
30708               M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height);
30709             } else {
30710               M = cimg::max(_width,_height,_depth,_spectrum);
30711               M = M==_width?cimg::max(_height,_depth,_spectrum):
30712                 M==_height?cimg::max(_width,_depth,_spectrum):
30713                 M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth);
30714             }
30715 
30716             bool do_in_parallel = false;
30717 #if cimg_use_openmp!=0
30718             cimg_openmp_if(*expression=='*' || *expression==':' ||
30719                            (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2))
30720               do_in_parallel = true;
30721 #endif
30722             if (mp.result_dim) { // Vector-valued expression
30723               const unsigned int N = std::min(mp.result_dim,_spectrum);
30724               const ulongT whd = (ulongT)_width*_height*_depth;
30725               T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
30726               if (*expression=='<') {
30727                 CImg<doubleT> res(1,mp.result_dim);
30728                 mp.begin_t();
30729                 cimg_rofYZ(*this,y,z) {
30730                   cimg_abort_test;
30731                   if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0);
30732                   else cimg_rofX(*this,x) {
30733                       mp(x,y,z,0,res._data);
30734                       const double *ptrs = res._data;
30735                       T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
30736                     }
30737                 }
30738                 mp.end_t();
30739 
30740               } else if (*expression=='>' || !do_in_parallel) {
30741                 CImg<doubleT> res(1,mp.result_dim);
30742                 mp.begin_t();
30743                 cimg_forYZ(*this,y,z) {
30744                   cimg_abort_test;
30745                   if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0);
30746                   else cimg_forX(*this,x) {
30747                       mp(x,y,z,0,res._data);
30748                       const double *ptrs = res._data;
30749                       T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
30750                     }
30751                 }
30752                 mp.end_t();
30753 
30754               } else {
30755 
30756 #if cimg_use_openmp!=0
30757                 unsigned int tid = 0;
30758                 cimg_pragma_openmp(parallel)
30759                 {
30760                   _cimg_math_parser *_mp = 0;
30761                   cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; }
30762                   _cimg_math_parser &lmp = *_mp;
30763                   lmp.is_fill = true;
30764                   cimg_pragma_openmp(barrier)
30765                   lmp.begin_t();
30766 
30767 #define _cimg_fill_openmp_vector(_YZ,_y,_z,_X,_x,_sx,_sy,_sz,_off) \
30768   cimg_pragma_openmp(for cimg_openmp_collapse(2)) \
30769   cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \
30770     cimg_abort_test; \
30771     if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,0); \
30772     else { \
30773       CImg<doubleT> res(1,lmp.result_dim); \
30774       T *__ptrd = data(_sx,_sy,_sz,0); \
30775       const ulongT off = (ulongT)_off; \
30776       cimg_for##_X(*this,_x) { \
30777         lmp(x,y,z,0,res._data); \
30778         const double *ptrs = res._data; \
30779         T *_ptrd = __ptrd; \
30780         for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } \
30781         __ptrd+=off; \
30782       } \
30783     } \
30784   } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
30785 
30786                   if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) }
30787                   else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) }
30788                   else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) }
30789 
30790                   lmp.end_t();
30791                   cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
30792                   if (&lmp!=&mp) delete &lmp;
30793                 }
30794 #endif
30795               }
30796 
30797             } else { // Scalar-valued expression
30798               T *ptrd = *expression=='<'?end() - 1:_data;
30799               if (*expression=='<') {
30800                 mp.begin_t();
30801                 if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); }
30802                 else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
30803                 mp.end_t();
30804 
30805               } else if (*expression=='>' || !do_in_parallel) {
30806                 mp.begin_t();
30807                 if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); }
30808                 else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
30809                 mp.end_t();
30810 
30811               } else {
30812 
30813 #if cimg_use_openmp!=0
30814                 unsigned int tid = 0;
30815                 cimg_pragma_openmp(parallel)
30816                 {
30817                   _cimg_math_parser *_mp = 0;
30818                   cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; }
30819                   _cimg_math_parser &lmp = *_mp;
30820                   lmp.is_fill = true;
30821                   cimg_pragma_openmp(barrier)
30822                   lmp.begin_t();
30823 
30824 #define _cimg_fill_openmp_scalar(_YZC,_y,_z,_c,_X,_x,_sx,_sy,_sz,_sc,_off) \
30825   cimg_pragma_openmp(for cimg_openmp_collapse(3)) \
30826   cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \
30827     cimg_abort_test; \
30828     if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,c); \
30829     else { \
30830       T *_ptrd = data(_sx,_sy,_sz,_sc); \
30831       const ulongT off = (ulongT)_off; \
30832       cimg_for##_X(*this,_x) { *_ptrd = (T)lmp(x,y,z,c); _ptrd+=off; } \
30833     } \
30834   } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
30835 
30836                   if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) }
30837                   else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) }
30838                   else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) }
30839                   else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) }
30840 
30841                   lmp.end_t();
30842                   cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
30843                   if (&lmp!=&mp) delete &lmp;
30844                 }
30845 #endif
30846               }
30847             }
30848             mp.end();
30849           } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error); }
30850       }
30851 
30852       // Try to fill values according to a value sequence.
30853       if (!formula_mode || is_value_sequence || is_error) {
30854         CImg<charT> item(256);
30855         char sep = 0;
30856         const char *nexpression = expression;
30857         ulongT nb = 0;
30858         const ulongT siz = size();
30859         T *ptrd = _data;
30860         for (double val = 0; *nexpression && nb<siz; ++nb) {
30861           sep = 0;
30862           const int err = cimg_sscanf(nexpression,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep);
30863           if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) {
30864             nexpression+=std::strlen(item) + (err>1);
30865             *(ptrd++) = (T)val;
30866           } else break;
30867         }
30868         cimg::exception_mode(omode);
30869         if (nb<siz && (sep || *nexpression)) {
30870           if (is_error) throw CImgArgumentException("%s",is_error._data);
30871           else throw CImgArgumentException(_cimg_instance
30872                                            "%s(): Invalid sequence of filling values '%s'.",
30873                                            cimg_instance,calling_function,expression);
30874         }
30875         if (repeat_values && nb && nb<siz)
30876           for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
30877       }
30878 
30879       cimg::exception_mode(omode);
30880       cimg_abort_test;
30881       return *this;
30882     }
30883 
30884     //! Fill sequentially pixel values according to a given expression \newinstance.
30885     CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
30886                      const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
30887       return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs);
30888     }
30889 
30890     //! Fill sequentially pixel values according to the values found in another image.
30891     /**
30892        \param values Image containing the values used for the filling.
30893        \param repeat_values In case there are less values than necessary in \c values, tells if these values must be
30894          repeated for the filling.
30895     **/
30896     template<typename t>
30897     CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
30898       if (is_empty() || !values) return *this;
30899       T *ptrd = _data, *ptre = ptrd + size();
30900       for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
30901         *(ptrd++) = (T)*ptrs;
30902       if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
30903       return *this;
30904     }
30905 
30906     //! Fill sequentially pixel values according to the values found in another image \newinstance.
30907     template<typename t>
30908     CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
30909       return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
30910         (+*this).fill(values,repeat_values);
30911     }
30912 
30913     //! Fill pixel values along the X-axis at a specified pixel position.
30914     /**
30915        \param y Y-coordinate of the filled column.
30916        \param z Z-coordinate of the filled column.
30917        \param c C-coordinate of the filled column.
30918        \param a0 First fill value.
30919     **/
30920     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
30921 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
30922     va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
30923     for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
30924     va_end(ap); }
30925       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
30926       return *this;
30927     }
30928 
30929     //! Fill pixel values along the X-axis at a specified pixel position \overloading.
30930     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
30931       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
30932       return *this;
30933     }
30934 
30935     //! Fill pixel values along the Y-axis at a specified pixel position.
30936     /**
30937        \param x X-coordinate of the filled row.
30938        \param z Z-coordinate of the filled row.
30939        \param c C-coordinate of the filled row.
30940        \param a0 First fill value.
30941     **/
30942     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
30943       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
30944       return *this;
30945     }
30946 
30947     //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
30948     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
30949       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
30950       return *this;
30951     }
30952 
30953     //! Fill pixel values along the Z-axis at a specified pixel position.
30954     /**
30955        \param x X-coordinate of the filled slice.
30956        \param y Y-coordinate of the filled slice.
30957        \param c C-coordinate of the filled slice.
30958        \param a0 First fill value.
30959     **/
30960     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
30961       const ulongT wh = (ulongT)_width*_height;
30962       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
30963       return *this;
30964     }
30965 
30966     //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
30967     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
30968       const ulongT wh = (ulongT)_width*_height;
30969       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
30970       return *this;
30971     }
30972 
30973     //! Fill pixel values along the C-axis at a specified pixel position.
30974     /**
30975        \param x X-coordinate of the filled channel.
30976        \param y Y-coordinate of the filled channel.
30977        \param z Z-coordinate of the filled channel.
30978        \param a0 First filling value.
30979     **/
30980     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
30981       const ulongT whd = (ulongT)_width*_height*_depth;
30982       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
30983       return *this;
30984     }
30985 
30986     //! Fill pixel values along the C-axis at a specified pixel position \overloading.
30987     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
30988       const ulongT whd = (ulongT)_width*_height*_depth;
30989       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
30990       return *this;
30991     }
30992 
30993     //! Discard specified sequence of values in the image buffer, along a specific axis.
30994     /**
30995        \param values Sequence of values to discard.
30996        \param axis Axis along which the values are discarded. If set to \c 0 (default value)
30997          the method does it for all the buffer values and returns a one-column vector.
30998        \note Discarded values will change the image geometry, so the resulting image
30999          is returned as a one-column vector.
31000     **/
31001     template<typename t>
31002     CImg<T>& discard(const CImg<t>& values, const char axis=0) {
31003       if (is_empty() || !values) return *this;
31004       return get_discard(values,axis).move_to(*this);
31005     }
31006 
31007     template<typename t>
31008     CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
31009       if (!values) return +*this;
31010       CImg<T> res;
31011       if (is_empty()) return res;
31012       const ulongT vsiz = values.size();
31013       const char _axis = cimg::lowercase(axis);
31014       ulongT j = 0;
31015       unsigned int k = 0;
31016       int i0 = 0;
31017       res.assign(width(),height(),depth(),spectrum());
31018       switch (_axis) {
31019       case 'x' : {
31020         cimg_forX(*this,i) {
31021           if ((*this)(i)!=(T)values[j]) {
31022             if (j) --i;
31023             res.draw_image(k,get_columns(i0,i));
31024             k+=i - i0 + 1; i0 = i + 1; j = 0;
31025           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31026         }
31027         if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
31028         res.resize(k,-100,-100,-100,0);
31029       } break;
31030       case 'y' : {
31031         cimg_forY(*this,i) {
31032           if ((*this)(0,i)!=(T)values[j]) {
31033             if (j) --i;
31034             res.draw_image(0,k,get_rows(i0,i));
31035             k+=i - i0 + 1; i0 = i + 1; j = 0;
31036           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31037         }
31038         if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
31039         res.resize(-100,k,-100,-100,0);
31040       } break;
31041       case 'z' : {
31042         cimg_forZ(*this,i) {
31043           if ((*this)(0,0,i)!=(T)values[j]) {
31044             if (j) --i;
31045             res.draw_image(0,0,k,get_slices(i0,i));
31046             k+=i - i0 + 1; i0 = i + 1; j = 0;
31047           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31048         }
31049         if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
31050         res.resize(-100,-100,k,-100,0);
31051       } break;
31052       case 'c' : {
31053         cimg_forC(*this,i) {
31054           if ((*this)(0,0,0,i)!=(T)values[j]) {
31055             if (j) --i;
31056             res.draw_image(0,0,0,k,get_channels(i0,i));
31057             k+=i - i0 + 1; i0 = i + 1; j = 0;
31058           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
31059         }
31060         if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
31061         res.resize(-100,-100,-100,k,0);
31062       } break;
31063       default : {
31064         const ulongT siz = size();
31065         res.unroll('y');
31066         if (vsiz==1) { // Optimized version for a single discard value
31067           const T val = (T)values[0];
31068           cimg_foroff(*this,i) {
31069             const T _val = (T)_data[i];
31070             if (_val!=val) res[k++] = _val;
31071           }
31072         } else { // Generic version
31073           cimg_foroff(*this,i) {
31074             if ((*this)[i]!=(T)values[j]) {
31075               if (j) --i;
31076               std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
31077               k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
31078             } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
31079           }
31080           if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
31081         }
31082         res.resize(1,k,1,1,0);
31083       }
31084       }
31085       return res;
31086     }
31087 
31088     //! Discard neighboring duplicates in the image buffer, along the specified axis.
31089     CImg<T>& discard(const char axis=0) {
31090       return get_discard(axis).move_to(*this);
31091     }
31092 
31093     //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance.
31094     CImg<T> get_discard(const char axis=0) const {
31095       CImg<T> res;
31096       if (is_empty()) return res;
31097       const char _axis = cimg::lowercase(axis);
31098       T current = *_data?(T)0:(T)1;
31099       int j = 0;
31100       res.assign(width(),height(),depth(),spectrum());
31101       switch (_axis) {
31102       case 'x' : {
31103         cimg_forX(*this,i)
31104           if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); }
31105         res.resize(j,-100,-100,-100,0);
31106       } break;
31107       case 'y' : {
31108         cimg_forY(*this,i)
31109           if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); }
31110         res.resize(-100,j,-100,-100,0);
31111       } break;
31112       case 'z' : {
31113         cimg_forZ(*this,i)
31114           if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); }
31115         res.resize(-100,-100,j,-100,0);
31116       } break;
31117       case 'c' : {
31118         cimg_forC(*this,i)
31119           if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); }
31120         res.resize(-100,-100,-100,j,0);
31121       } break;
31122       default : {
31123         res.unroll('y');
31124         cimg_foroff(*this,i) {
31125           const T val = (*this)[i];
31126           if (val!=current) res[j++] = current = val;
31127         }
31128         res.resize(-100,j,-100,-100,0);
31129       }
31130       }
31131       return res;
31132     }
31133 
31134     //! Invert endianness of all pixel values.
31135     /**
31136      **/
31137     CImg<T>& invert_endianness() {
31138       cimg::invert_endianness(_data,size());
31139       return *this;
31140     }
31141 
31142     //! Invert endianness of all pixel values \newinstance.
31143     CImg<T> get_invert_endianness() const {
31144       return (+*this).invert_endianness();
31145     }
31146 
31147     //! Fill image with random values in specified range.
31148     /**
31149        \param val_min Minimal authorized random value.
31150        \param val_max Maximal authorized random value.
31151        \note Random variables are uniformly distributed in [val_min,val_max].
31152      **/
31153     CImg<T>& rand(const T& val_min, const T& val_max) {
31154       const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1);
31155       if (cimg::type<T>::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
31156           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31157 
31158 #if cimg_use_openmp!=0
31159           rng+=omp_get_thread_num();
31160 #endif
31161           cimg_pragma_openmp(for)
31162             cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng));
31163           cimg::srand(rng);
31164         } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
31165           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31166 
31167 #if cimg_use_openmp!=0
31168           rng+=omp_get_thread_num();
31169 #endif
31170           cimg_pragma_openmp(for)
31171             cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng)));
31172           cimg::srand(rng);
31173         }
31174       return *this;
31175     }
31176 
31177     //! Fill image with random values in specified range \newinstance.
31178     CImg<T> get_rand(const T& val_min, const T& val_max) const {
31179       return (+*this).rand(val_min,val_max);
31180     }
31181 
31182     //! Round pixel values.
31183     /**
31184        \param y Rounding precision.
31185        \param rounding_type Rounding type. Can be:
31186        - \c -1: Backward.
31187        - \c 0: Nearest.
31188        - \c 1: Forward.
31189     **/
31190     CImg<T>& round(const double y=1, const int rounding_type=0) {
31191       if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192);
31192       return *this;
31193     }
31194 
31195     //! Round pixel values \newinstance.
31196     CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
31197       return (+*this).round(y,rounding_type);
31198     }
31199 
31200     //! Add random noise to pixel values.
31201     /**
31202        \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the
31203          global value range.
31204        \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper,
31205          \p 3=Poisson or \p 4=Rician).
31206        \return A reference to the modified image instance.
31207        \note
31208        - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on
31209          the image value itself.
31210        - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
31211        \par Example
31212        \code
31213        const CImg<float> img("reference.jpg"), res = img.get_noise(40);
31214        (img,res.normalize(0,255)).display();
31215        \endcode
31216        \image html ref_noise.jpg
31217     **/
31218     CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
31219       if (is_empty()) return *this;
31220       const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
31221       Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
31222       if (nsigma==0 && noise_type!=3) return *this;
31223       if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
31224       if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.);
31225       switch (noise_type) {
31226       case 0 : { // Gaussian noise
31227         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31228           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31229 
31230 #if cimg_use_openmp!=0
31231           rng+=omp_get_thread_num();
31232 #endif
31233           cimg_pragma_openmp(for)
31234             cimg_rofoff(*this,off) {
31235             Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng));
31236             if (val>vmax) val = vmax;
31237             if (val<vmin) val = vmin;
31238             _data[off] = (T)val;
31239           }
31240           cimg::srand(rng);
31241         }
31242       } break;
31243       case 1 : { // Uniform noise
31244         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31245           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31246 
31247 #if cimg_use_openmp!=0
31248           rng+=omp_get_thread_num();
31249 #endif
31250           cimg_pragma_openmp(for)
31251             cimg_rofoff(*this,off) {
31252             Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::rand(-1,1,&rng));
31253             if (val>vmax) val = vmax;
31254             if (val<vmin) val = vmin;
31255             _data[off] = (T)val;
31256           }
31257           cimg::srand(rng);
31258         }
31259       } break;
31260       case 2 : { // Salt & Pepper noise
31261         if (nsigma<0) nsigma = -nsigma;
31262         if (M==m) {
31263           if (cimg::type<T>::is_float()) { --m; ++M; }
31264           else { m = (Tfloat)cimg::type<T>::min(); M = (Tfloat)cimg::type<T>::max(); }
31265         }
31266         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31267           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31268 
31269 #if cimg_use_openmp!=0
31270           rng+=omp_get_thread_num();
31271 #endif
31272           cimg_pragma_openmp(for)
31273             cimg_rofoff(*this,off) if (cimg::rand(100,&rng)<nsigma) _data[off] = (T)(cimg::rand(1,&rng)<0.5?M:m);
31274           cimg::srand(rng);
31275           }
31276       } break;
31277       case 3 : { // Poisson Noise
31278         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31279           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31280 
31281 #if cimg_use_openmp!=0
31282           rng+=omp_get_thread_num();
31283 #endif
31284           cimg_pragma_openmp(for)
31285             cimg_rofoff(*this,off) _data[off] = (T)cimg::prand(_data[off],&rng);
31286           cimg::srand(rng);
31287         }
31288       } break;
31289       case 4 : { // Rice noise
31290         const Tfloat sqrt2 = (Tfloat)std::sqrt(2.);
31291         cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
31292           cimg_uint64 rng = (cimg::_rand(),cimg::rng());
31293 
31294 #if cimg_use_openmp!=0
31295           rng+=omp_get_thread_num();
31296 #endif
31297           cimg_pragma_openmp(for)
31298             cimg_rofoff(*this,off) {
31299             const Tfloat
31300               val0 = (Tfloat)_data[off]/sqrt2,
31301               re = (Tfloat)(val0 + nsigma*cimg::grand(&rng)),
31302               im = (Tfloat)(val0 + nsigma*cimg::grand(&rng));
31303             Tfloat val = cimg::hypot(re,im);
31304             if (val>vmax) val = vmax;
31305             if (val<vmin) val = vmin;
31306             _data[off] = (T)val;
31307           }
31308           cimg::srand(rng);
31309         }
31310       } break;
31311       default :
31312         throw CImgArgumentException(_cimg_instance
31313                                     "noise(): Invalid specified noise type %d "
31314                                     "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
31315                                     cimg_instance,
31316                                     noise_type);
31317       }
31318       return *this;
31319     }
31320 
31321     //! Add random noise to pixel values \newinstance.
31322     CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
31323       return (+*this).noise(sigma,noise_type);
31324     }
31325 
31326     //! Linearly normalize pixel values.
31327     /**
31328        \param min_value Minimum desired value of the resulting image.
31329        \param max_value Maximum desired value of the resulting image.
31330        \param constant_case_ratio In case of instance image having a constant value, tell what ratio
31331               of [min_value,max_value] is used to fill the normalized image
31332               (=0 for min_value, =1 for max_value, =0.5 for (min_value + max_value)/2).
31333        \par Example
31334        \code
31335        const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
31336        (img,res).display();
31337        \endcode
31338        \image html ref_normalize2.jpg
31339     **/
31340     CImg<T>& normalize(const T& min_value, const T& max_value,
31341                        const float constant_case_ratio=0) {
31342       if (is_empty()) return *this;
31343       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
31344       T m, M = max_min(m);
31345       const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
31346       if (m==M)
31347         return fill(constant_case_ratio==0?a:
31348                     constant_case_ratio==1?b:
31349                     (T)((1 - constant_case_ratio)*a + constant_case_ratio*b));
31350       if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a);
31351       return *this;
31352     }
31353 
31354     //! Linearly normalize pixel values \newinstance.
31355     CImg<Tfloat> get_normalize(const T& min_value, const T& max_value,
31356                                const float ratio_if_constant_image=0) const {
31357       return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value,ratio_if_constant_image);
31358     }
31359 
31360     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
31361     /**
31362        \par Example
31363        \code
31364        const CImg<float> img("reference.jpg"), res = img.get_normalize();
31365        (img,res.normalize(0,255)).display();
31366        \endcode
31367        \image html ref_normalize.jpg
31368     **/
31369     CImg<T>& normalize() {
31370       const ulongT whd = (ulongT)_width*_height*_depth;
31371       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31372                                                                  _height*_depth>=16))
31373       cimg_forYZ(*this,y,z) {
31374         T *ptrd = data(0,y,z,0);
31375         cimg_forX(*this,x) {
31376           const T *ptrs = ptrd;
31377           float n = 0;
31378           cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
31379           n = (float)std::sqrt(n);
31380           T *_ptrd = ptrd++;
31381           if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
31382           else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
31383         }
31384       }
31385       return *this;
31386     }
31387 
31388     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
31389     CImg<Tfloat> get_normalize() const {
31390       return CImg<Tfloat>(*this,false).normalize();
31391     }
31392 
31393     //! Compute Lp-norm of each multi-valued pixel of the image instance.
31394     /**
31395        \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0).
31396        \par Example
31397        \code
31398        const CImg<float> img("reference.jpg"), res = img.get_norm();
31399        (img,res.normalize(0,255)).display();
31400        \endcode
31401        \image html ref_norm.jpg
31402     **/
31403     CImg<T>& norm(const int norm_type=2) {
31404       if (_spectrum==1 && norm_type) return abs();
31405       return get_norm(norm_type).move_to(*this);
31406     }
31407 
31408     //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
31409     CImg<Tfloat> get_norm(const int norm_type=2) const {
31410       if (is_empty()) return *this;
31411       if (_spectrum==1 && norm_type) return get_abs();
31412       const ulongT whd = (ulongT)_width*_height*_depth;
31413       CImg<Tfloat> res(_width,_height,_depth);
31414       switch (norm_type) {
31415       case -1 : { // Linf-norm
31416         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31417                                                                    _height*_depth>=16))
31418         cimg_forYZ(*this,y,z) {
31419           const ulongT off = (ulongT)offset(0,y,z);
31420           const T *ptrs = _data + off;
31421           Tfloat *ptrd = res._data + off;
31422           cimg_forX(*this,x) {
31423             Tfloat n = 0;
31424             const T *_ptrs = ptrs++;
31425             cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
31426             *(ptrd++) = n;
31427           }
31428         }
31429       } break;
31430       case 0 : { // L0-norm
31431         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31432                                                                    _height*_depth>=16))
31433         cimg_forYZ(*this,y,z) {
31434           const ulongT off = (ulongT)offset(0,y,z);
31435           const T *ptrs = _data + off;
31436           Tfloat *ptrd = res._data + off;
31437           cimg_forX(*this,x) {
31438             unsigned int n = 0;
31439             const T *_ptrs = ptrs++;
31440             cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; }
31441             *(ptrd++) = (Tfloat)n;
31442           }
31443         }
31444       } break;
31445       case 1 : { // L1-norm
31446         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31447                                                                    _height*_depth>=16))
31448         cimg_forYZ(*this,y,z) {
31449           const ulongT off = (ulongT)offset(0,y,z);
31450           const T *ptrs = _data + off;
31451           Tfloat *ptrd = res._data + off;
31452           cimg_forX(*this,x) {
31453             Tfloat n = 0;
31454             const T *_ptrs = ptrs++;
31455             cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
31456             *(ptrd++) = n;
31457           }
31458         }
31459       } break;
31460       case 2 : { // L2-norm
31461         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31462                                                                    _height*_depth>=16))
31463         cimg_forYZ(*this,y,z) {
31464           const ulongT off = (ulongT)offset(0,y,z);
31465           const T *ptrs = _data + off;
31466           Tfloat *ptrd = res._data + off;
31467           cimg_forX(*this,x) {
31468             Tfloat n = 0;
31469             const T *_ptrs = ptrs++;
31470             cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
31471             *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
31472           }
31473         }
31474       } break;
31475       default : { // Linf-norm
31476         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
31477                                                                    _height*_depth>=16))
31478         cimg_forYZ(*this,y,z) {
31479           const ulongT off = (ulongT)offset(0,y,z);
31480           const T *ptrs = _data + off;
31481           Tfloat *ptrd = res._data + off;
31482           cimg_forX(*this,x) {
31483             Tfloat n = 0;
31484             const T *_ptrs = ptrs++;
31485             cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; }
31486             *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type);
31487           }
31488         }
31489       }
31490       }
31491       return res;
31492     }
31493 
31494     //! Cut pixel values in specified range.
31495     /**
31496        \param min_value Minimum desired value of the resulting image.
31497        \param max_value Maximum desired value of the resulting image.
31498        \par Example
31499        \code
31500        const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
31501        (img,res).display();
31502        \endcode
31503        \image html ref_cut.jpg
31504     **/
31505     CImg<T>& cut(const T& min_value, const T& max_value) {
31506       if (is_empty()) return *this;
31507       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
31508       cimg_openmp_for(*this,cimg::cut(*ptr,a,b),32768);
31509       return *this;
31510     }
31511 
31512     //! Cut pixel values in specified range \newinstance.
31513     CImg<T> get_cut(const T& min_value, const T& max_value) const {
31514       return (+*this).cut(min_value,max_value);
31515     }
31516 
31517     //! Uniformly quantize pixel values.
31518     /**
31519        \param nb_levels Number of quantization levels.
31520        \param keep_range Tells if resulting values keep the same range as the original ones.
31521        \par Example
31522        \code
31523        const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
31524        (img,res).display();
31525        \endcode
31526        \image html ref_quantize.jpg
31527     **/
31528     CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
31529       if (!nb_levels)
31530         throw CImgArgumentException(_cimg_instance
31531                                     "quantize(): Invalid quantization request with 0 values.",
31532                                     cimg_instance);
31533 
31534       if (is_empty()) return *this;
31535       Tfloat m, M = (Tfloat)max_min(m), range = M - m;
31536       if (range>0) {
31537         if (keep_range)
31538           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
31539           cimg_rofoff(*this,off) {
31540             const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
31541             _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels);
31542           } else
31543           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
31544           cimg_rofoff(*this,off) {
31545             const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
31546             _data[off] = (T)std::min(val,nb_levels - 1);
31547           }
31548       }
31549       return *this;
31550     }
31551 
31552     //! Uniformly quantize pixel values \newinstance.
31553     CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
31554       return (+*this).quantize(n,keep_range);
31555     }
31556 
31557     //! Threshold pixel values.
31558     /**
31559        \param value Threshold value
31560        \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
31561        \param strict_threshold Tells if threshold value is strict.
31562        \par Example
31563        \code
31564        const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
31565        (img,res.normalize(0,255)).display();
31566        \endcode
31567        \image html ref_threshold.jpg
31568     **/
31569     CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
31570       if (is_empty()) return *this;
31571       if (strict_threshold) {
31572         if (soft_threshold)
31573           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
31574           cimg_rofoff(*this,off) {
31575             const T v = _data[off];
31576             _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
31577           }
31578         else
31579           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
31580           cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0;
31581       } else {
31582         if (soft_threshold)
31583           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
31584           cimg_rofoff(*this,off) {
31585             const T v = _data[off];
31586             _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
31587           }
31588         else
31589           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
31590           cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0;
31591       }
31592       return *this;
31593     }
31594 
31595     //! Threshold pixel values \newinstance.
31596     CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const {
31597       return (+*this).threshold(value,soft_threshold,strict_threshold);
31598     }
31599 
31600     //! Compute the histogram of pixel values.
31601     /**
31602        \param nb_levels Number of desired histogram levels.
31603        \param min_value Minimum pixel value considered for the histogram computation.
31604          All pixel values lower than \p min_value will not be counted.
31605        \param max_value Maximum pixel value considered for the histogram computation.
31606          All pixel values higher than \p max_value will not be counted.
31607        \note
31608        - The histogram H of an image I is the 1D function where H(x) counts the number of occurrences of the value x
31609          in the image I.
31610        - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional.
31611        \par Example
31612        \code
31613        const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
31614        img.display_graph(0,3);
31615        \endcode
31616        \image html ref_histogram.jpg
31617     **/
31618     CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
31619       return get_histogram(nb_levels,min_value,max_value).move_to(*this);
31620     }
31621 
31622     //! Compute the histogram of pixel values \overloading.
31623     CImg<T>& histogram(const unsigned int nb_levels) {
31624       return get_histogram(nb_levels).move_to(*this);
31625     }
31626 
31627     //! Compute the histogram of pixel values \newinstance.
31628     CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const {
31629       if (!nb_levels || is_empty()) return CImg<ulongT>();
31630       const double
31631         vmin = (double)(min_value<max_value?min_value:max_value),
31632         vmax = (double)(min_value<max_value?max_value:min_value);
31633       CImg<ulongT> res(nb_levels,1,1,1,0);
31634       cimg_rof(*this,ptrs,T) {
31635         const T val = *ptrs;
31636         if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))];
31637       }
31638       return res;
31639     }
31640 
31641     //! Compute the histogram of pixel values \newinstance.
31642     CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
31643       if (!nb_levels || is_empty()) return CImg<ulongT>();
31644       T vmax = 0, vmin = min_max(vmax);
31645       return get_histogram(nb_levels,vmin,vmax);
31646     }
31647 
31648     //! Equalize histogram of pixel values.
31649     /**
31650        \param nb_levels Number of histogram levels used for the equalization.
31651        \param min_value Minimum pixel value considered for the histogram computation.
31652          All pixel values lower than \p min_value will not be counted.
31653        \param max_value Maximum pixel value considered for the histogram computation.
31654          All pixel values higher than \p max_value will not be counted.
31655        \par Example
31656        \code
31657        const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
31658        (img,res).display();
31659        \endcode
31660        \image html ref_equalize.jpg
31661     **/
31662     CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) {
31663       if (!nb_levels || is_empty()) return *this;
31664       const T
31665         vmin = min_value<max_value?min_value:max_value,
31666         vmax = min_value<max_value?max_value:min_value;
31667       CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
31668       ulongT cumul = 0;
31669       cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
31670       if (!cumul) cumul = 1;
31671       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576))
31672       cimg_rofoff(*this,off) {
31673         const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin));
31674         if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul);
31675       }
31676       return *this;
31677     }
31678 
31679     //! Equalize histogram of pixel values \overloading.
31680     CImg<T>& equalize(const unsigned int nb_levels) {
31681       if (!nb_levels || is_empty()) return *this;
31682       T vmax = 0, vmin = min_max(vmax);
31683       return equalize(nb_levels,vmin,vmax);
31684     }
31685 
31686     //! Equalize histogram of pixel values \newinstance.
31687     CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const {
31688       return (+*this).equalize(nblevels,val_min,val_max);
31689     }
31690 
31691     //! Equalize histogram of pixel values \newinstance.
31692     CImg<T> get_equalize(const unsigned int nblevels) const {
31693       return (+*this).equalize(nblevels);
31694     }
31695 
31696     //! Index multi-valued pixels regarding to a specified colormap.
31697     /**
31698        \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
31699        \param dithering Level of dithering (0=disable, 1=standard level).
31700        \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
31701        \note
31702        - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
31703        \par Example
31704        \code
31705        const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
31706        const CImg<float> res = img.get_index(colormap,1,true);
31707        (img,res).display();
31708        \endcode
31709        \image html ref_index.jpg
31710     **/
31711     template<typename t>
31712     CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
31713       return get_index(colormap,dithering,map_indexes).move_to(*this);
31714     }
31715 
31716     //! Index multi-valued pixels regarding to a specified colormap \newinstance.
31717     template<typename t>
31718     CImg<typename CImg<t>::Tuint>
31719     get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
31720       if (colormap._spectrum!=_spectrum)
31721         throw CImgArgumentException(_cimg_instance
31722                                     "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
31723                                     "have incompatible dimensions.",
31724                                     cimg_instance,
31725                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
31726 
31727       typedef typename CImg<t>::Tuint tuint;
31728       if (is_empty()) return CImg<tuint>();
31729       const ulongT
31730         whd = (ulongT)_width*_height*_depth,
31731         pwhd = (ulongT)colormap._width*colormap._height*colormap._depth;
31732       CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
31733       if (dithering>0) { // Dithered versions
31734         tuint *ptrd = res._data;
31735         const float ndithering = cimg::cut(dithering,0,1)/16;
31736         Tfloat valm = 0, valM = (Tfloat)max_min(valm);
31737         if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
31738         CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1);
31739         Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
31740         const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth;
31741         switch (_spectrum) {
31742         case 1 : { // Optimized for scalars
31743           cimg_forYZ(*this,y,z) {
31744             if (y<height() - 2) {
31745               Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0);
31746               cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
31747             }
31748             Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
31749             cimg_forX(*this,x) {
31750               const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
31751               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
31752               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
31753                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
31754                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
31755               }
31756               const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
31757               *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
31758               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
31759             }
31760             cimg::swap(cache_current,cache_next);
31761           }
31762         } break;
31763         case 2 : { // Optimized for 2D vectors
31764           tuint *ptrd1 = ptrd + whd;
31765           cimg_forYZ(*this,y,z) {
31766             if (y<height() - 2) {
31767               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
31768               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd;
31769               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
31770             }
31771             Tfloat
31772               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
31773               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
31774             cimg_forX(*this,x) {
31775               const Tfloat
31776                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
31777                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
31778               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
31779               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
31780                 const Tfloat
31781                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
31782                   dist = pval0*pval0 + pval1*pval1;
31783                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
31784               }
31785               const t *const ptrmin1 = ptrmin0 + pwhd;
31786               const Tfloat
31787                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
31788                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
31789               *ptrs0+=7*err0; *ptrs1+=7*err1;
31790               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1;
31791               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
31792               *ptrsn0+=err0; *ptrsn1+=err1;
31793               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
31794               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
31795             }
31796             cimg::swap(cache_current,cache_next);
31797           }
31798         } break;
31799         case 3 : { // Optimized for 3D vectors (colors)
31800           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
31801           cimg_forYZ(*this,y,z) {
31802             if (y<height() - 2) {
31803               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
31804               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
31805               cimg_forX(*this,x) {
31806                 *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
31807               }
31808             }
31809             Tfloat
31810               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
31811               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
31812             cimg_forX(*this,x) {
31813               const Tfloat
31814                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
31815                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
31816                 _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
31817               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
31818               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
31819                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
31820                 const Tfloat
31821                   pval0 = (Tfloat)*(ptrp0++) - val0,
31822                   pval1 = (Tfloat)*(ptrp1++) - val1,
31823                   pval2 = (Tfloat)*(ptrp2++) - val2,
31824                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
31825                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
31826               }
31827               const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
31828               const Tfloat
31829                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
31830                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
31831                 err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
31832 
31833               *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
31834               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2;
31835               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
31836               *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
31837 
31838               if (map_indexes) {
31839                 *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
31840               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
31841             }
31842             cimg::swap(cache_current,cache_next);
31843           }
31844         } break;
31845         default : // Generic version
31846           cimg_forYZ(*this,y,z) {
31847             if (y<height() - 2) {
31848               Tfloat *ptrc = cache_next;
31849               cimg_forC(*this,c) {
31850                 Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c);
31851                 cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
31852                 ptrc+=cwhd;
31853               }
31854             }
31855             Tfloat *ptrs = cache_current, *ptrsn = cache_next;
31856             cimg_forX(*this,x) {
31857               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
31858               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
31859                 Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
31860                 cimg_forC(*this,c) {
31861                   const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
31862                   dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
31863                 }
31864                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
31865               }
31866               const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1;
31867               cimg_forC(*this,c) {
31868                 const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
31869                 *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
31870                 _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2;
31871               }
31872               if (map_indexes) {
31873                 tuint *_ptrd = ptrd++;
31874                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
31875               }
31876               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
31877             }
31878             cimg::swap(cache_current,cache_next);
31879           }
31880         }
31881       } else { // Non-dithered versions
31882         switch (_spectrum) {
31883         case 1 : { // Optimized for scalars
31884           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
31885                                                                      _height*_depth>=16 && pwhd>=16))
31886           cimg_forYZ(*this,y,z) {
31887             tuint *ptrd = res.data(0,y,z);
31888             for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
31889               const Tfloat val0 = (Tfloat)*(ptrs0++);
31890               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
31891               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
31892                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
31893                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
31894               }
31895               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
31896             }
31897           }
31898         } break;
31899         case 2 : { // Optimized for 2D vectors
31900           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
31901                                                                      _height*_depth>=16 && pwhd>=16))
31902           cimg_forYZ(*this,y,z) {
31903             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
31904             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
31905               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
31906               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
31907               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
31908                 const Tfloat
31909                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
31910                   dist = pval0*pval0 + pval1*pval1;
31911                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
31912               }
31913               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
31914               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
31915             }
31916           }
31917         } break;
31918         case 3 : { // Optimized for 3D vectors (colors)
31919           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
31920                                                                      _height*_depth>=16 && pwhd>=16))
31921           cimg_forYZ(*this,y,z) {
31922             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
31923             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
31924                    *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
31925               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
31926               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
31927               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
31928                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
31929                 const Tfloat
31930                   pval0 = (Tfloat)*(ptrp0++) - val0,
31931                   pval1 = (Tfloat)*(ptrp1++) - val1,
31932                   pval2 = (Tfloat)*(ptrp2++) - val2,
31933                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
31934                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
31935               }
31936               if (map_indexes) {
31937                 *(ptrd++) = (tuint)*ptrmin0;
31938                 *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
31939                 *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
31940               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
31941             }
31942           }
31943         } break;
31944         default : // Generic version
31945           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
31946                                                                      _height*_depth>=16 && pwhd>=16))
31947           cimg_forYZ(*this,y,z) {
31948             tuint *ptrd = res.data(0,y,z);
31949             for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
31950               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
31951               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
31952                 Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
31953                 cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
31954                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
31955               }
31956               if (map_indexes) {
31957                 tuint *_ptrd = ptrd++;
31958                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
31959               }
31960               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
31961             }
31962           }
31963         }
31964       }
31965       return res;
31966     }
31967 
31968     //! Map predefined colormap on the scalar (indexed) image instance.
31969     /**
31970        \param colormap Multi-valued colormap used for mapping the indexes.
31971        \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
31972        \par Example
31973        \code
31974        const CImg<float> img("reference.jpg"),
31975                          colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
31976                          colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
31977                          res = img.get_index(colormap1,0).map(colormap2);
31978        (img,res).display();
31979        \endcode
31980        \image html ref_map.jpg
31981     **/
31982     template<typename t>
31983     CImg<T>& map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) {
31984       return get_map(colormap,boundary_conditions).move_to(*this);
31985     }
31986 
31987     //! Map predefined colormap on the scalar (indexed) image instance \newinstance.
31988     template<typename t>
31989     CImg<t> get_map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) const {
31990       const ulongT
31991         whd = (ulongT)_width*_height*_depth, siz = size(),
31992         cwhd = (ulongT)colormap._width*colormap._height*colormap._depth,
31993         cwhd2 = 2*cwhd;
31994       CImg<t> res(_width,_height,_depth,_spectrum*colormap._spectrum);
31995       switch (colormap._spectrum) {
31996 
31997       case 1 : { // Optimized for scalars
31998         switch (boundary_conditions) {
31999         case 3 : // Mirror
32000           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32001           for (longT off = 0; off<(longT)siz; ++off) {
32002             const ulongT ind = ((ulongT)_data[off])%cwhd2;
32003             res[off] = colormap[ind<cwhd?ind:cwhd2 - ind - 1];
32004           }
32005           break;
32006         case 2 : // Periodic
32007           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32008           for (longT off = 0; off<(longT)siz; ++off) {
32009             const ulongT ind = (ulongT)_data[off];
32010             res[off] = colormap[ind%cwhd];
32011           }
32012           break;
32013         case 1 : // Neumann
32014           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32015           for (longT off = 0; off<(longT)siz; ++off) {
32016             const longT ind = (longT)_data[off];
32017             res[off] = colormap[cimg::cut(ind,(longT)0,(longT)cwhd - 1)];
32018           } break;
32019         default : // Dirichlet
32020           cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32021           for (longT off = 0; off<(longT)siz; ++off) {
32022             const ulongT ind = (ulongT)_data[off];
32023             res[off] = ind<cwhd?colormap[ind]:(t)0;
32024           }
32025         }
32026       } break;
32027 
32028       case 2 : { // Optimized for 2D vectors
32029         const t *const ptrp0 = colormap._data, *const ptrp1 = ptrp0 + cwhd;
32030         switch (boundary_conditions) {
32031         case 3 : // Mirror
32032           cimg_forC(*this,c) {
32033             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32034             const T *const ptrs = data(0,0,0,c);
32035             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32036             for (longT off = 0; off<(longT)whd; ++off) {
32037               const ulongT
32038                 _ind = ((ulongT)ptrs[off])%cwhd2,
32039                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32040               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32041             }
32042           } break;
32043         case 2 : // Periodic
32044           cimg_forC(*this,c) {
32045             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32046             const T *const ptrs = data(0,0,0,c);
32047             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32048             for (longT off = 0; off<(longT)whd; ++off) {
32049               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32050               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32051             }
32052           } break;
32053         case 1 : // Neumann
32054           cimg_forC(*this,c) {
32055             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32056             const T *const ptrs = data(0,0,0,c);
32057             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32058             for (longT off = 0; off<(longT)whd; ++off) {
32059               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32060               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
32061             }
32062           } break;
32063         default : // Dirichlet
32064           cimg_forC(*this,c) {
32065             t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
32066             const T *const ptrs = data(0,0,0,c);
32067             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32068             for (longT off = 0; off<(longT)whd; ++off) {
32069               const ulongT ind = (ulongT)ptrs[off];
32070               if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; }
32071               else ptrd0[off] = ptrd1[off] = (t)0;
32072             }
32073           }
32074         }
32075       } break;
32076 
32077       case 3 : { // Optimized for 3D vectors (colors)
32078         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp0 + 2*cwhd;
32079         switch (boundary_conditions) {
32080         case 3 : // Mirror
32081           cimg_forC(*this,c) {
32082             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32083             const T *const ptrs = data(0,0,0,c);
32084             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32085             for (longT off = 0; off<(longT)whd; ++off) {
32086               const ulongT
32087                 _ind = ((ulongT)ptrs[off])%cwhd2,
32088                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32089               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32090             }
32091           } break;
32092         case 2 : // Periodic
32093           cimg_forC(*this,c) {
32094             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32095             const T *const ptrs = data(0,0,0,c);
32096             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32097             for (longT off = 0; off<(longT)whd; ++off) {
32098               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32099               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32100             }
32101           } break;
32102         case 1 : // Neumann
32103           cimg_forC(*this,c) {
32104             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32105             const T *const ptrs = data(0,0,0,c);
32106             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32107             for (longT off = 0; off<(longT)whd; ++off) {
32108               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32109               ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
32110             }
32111           } break;
32112         default : // Dirichlet
32113           cimg_forC(*this,c) {
32114             t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
32115             const T *const ptrs = data(0,0,0,c);
32116             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32117             for (longT off = 0; off<(longT)whd; ++off) {
32118               const ulongT ind = (ulongT)ptrs[off];
32119               if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind]; }
32120               else ptrd0[off] = ptrd1[off] = ptrd2[off] = (t)0;
32121             }
32122           }
32123         }
32124       } break;
32125 
32126       default : { // Generic version
32127         switch (boundary_conditions) {
32128         case 3 : // Mirror
32129           cimg_forC(*this,c) {
32130             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32131             const T *const ptrs = data(0,0,0,c);
32132             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32133             for (longT off = 0; off<(longT)whd; ++off) {
32134               const ulongT
32135                 _ind = ((ulongT)ptrs[off])%cwhd,
32136                 ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
32137               t *const _ptrd = ptrd + off;
32138               const t *const ptrp = &colormap[ind];
32139               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32140             }
32141           } break;
32142         case 2 : // Periodic
32143           cimg_forC(*this,c) {
32144             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32145             const T *const ptrs = data(0,0,0,c);
32146             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32147             for (longT off = 0; off<(longT)whd; ++off) {
32148               const ulongT ind = ((ulongT)ptrs[off])%cwhd;
32149               t *const _ptrd = ptrd + off;
32150               const t *const ptrp = &colormap[ind];
32151               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32152             }
32153           } break;
32154         case 1 : // Neumann
32155           cimg_forC(*this,c) {
32156             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32157             const T *const ptrs = data(0,0,0,c);
32158             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32159             for (longT off = 0; off<(longT)whd; ++off) {
32160               const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
32161               t *const _ptrd = ptrd + off;
32162               const t *const ptrp = &colormap[ind];
32163               cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32164             }
32165           } break;
32166         default : // Dirichlet
32167           cimg_forC(*this,c) {
32168             t *const ptrd = res.data(0,0,0,colormap._spectrum*c);
32169             const T *const ptrs = data(0,0,0,c);
32170             cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
32171             for (longT off = 0; off<(longT)whd; ++off) {
32172               const ulongT ind = (ulongT)ptrs[off];
32173               t *const _ptrd = ptrd + off;
32174               if (ind<cwhd) {
32175                 const t *const ptrp = &colormap[ind];
32176                 cimg_forC(colormap,k) _ptrd[k*whd] = ptrp[k*cwhd];
32177               } else cimg_forC(colormap,k) _ptrd[k*whd] = (t)0;
32178             }
32179           }
32180         }
32181       }
32182       }
32183       return res;
32184     }
32185 
32186     //! Label connected components.
32187     /**
32188        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
32189        in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
32190        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
32191        \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
32192        \note The algorithm of connected components computation has been primarily done
32193        by A. Meijster, according to the publication:
32194        'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
32195        In: Science of Computer Programming 41 (2001), pp. 173--194'.
32196        The submitted code has then been modified to fit CImg coding style and constraints.
32197     **/
32198     CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
32199                    const bool is_L2_norm=true) {
32200       if (is_empty()) return *this;
32201       return get_label(is_high_connectivity,tolerance,is_L2_norm).move_to(*this);
32202     }
32203 
32204     //! Label connected components \newinstance.
32205     CImg<ulongT> get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
32206                            const bool is_L2_norm=true) const {
32207       if (is_empty()) return CImg<ulongT>();
32208 
32209       // Create neighborhood tables.
32210       int dx[13], dy[13], dz[13], nb = 0;
32211       dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0;
32212       dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0;
32213       if (is_high_connectivity) {
32214         dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0;
32215         dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0;
32216       }
32217       if (_depth>1) { // 3D version
32218         dx[nb] = 0; dy[nb] = 0; dz[nb++]=1;
32219         if (is_high_connectivity) {
32220           dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1;
32221           dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1;
32222           dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1;
32223           dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1;
32224 
32225           dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1;
32226           dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1;
32227           dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1;
32228           dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1;
32229         }
32230       }
32231       return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
32232     }
32233 
32234     //! Label connected components \overloading.
32235     /**
32236        \param connectivity_mask Mask of the neighboring pixels.
32237        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
32238        \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
32239     **/
32240     template<typename t>
32241     CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
32242                    const bool is_L2_norm=true) {
32243       if (is_empty()) return *this;
32244       return get_label(connectivity_mask,tolerance,is_L2_norm).move_to(*this);
32245     }
32246 
32247     //! Label connected components \newinstance.
32248     template<typename t>
32249     CImg<ulongT> get_label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
32250                            const bool is_L2_norm=true) const {
32251       if (is_empty()) return CImg<ulongT>();
32252       int nb = 0;
32253       cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
32254       CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
32255       nb = 0;
32256       cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
32257                                                connectivity_mask(x,y,z)) {
32258         dx[nb] = x; dy[nb] = y; dz[nb++] = z;
32259       }
32260       return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
32261     }
32262 
32263     CImg<ulongT> _label(const unsigned int nb, const int *const dx,
32264                         const int *const dy, const int *const dz,
32265                         const Tfloat tolerance, const bool is_L2_norm) const {
32266       CImg<ulongT> res(_width,_height,_depth);
32267       const Tfloat _tolerance = _spectrum>1 && is_L2_norm?cimg::sqr(tolerance):tolerance;
32268 
32269       // Init label numbers.
32270       ulongT *ptr = res.data();
32271       cimg_foroff(res,p) *(ptr++) = p;
32272 
32273       // For each neighbour-direction, label.
32274       for (unsigned int n = 0; n<nb; ++n) {
32275         const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
32276         if (_dx || _dy || _dz) {
32277           const int
32278             x0 = _dx<0?-_dx:0,
32279             x1 = _dx<0?width():width() - _dx,
32280             y0 = _dy<0?-_dy:0,
32281             y1 = _dy<0?height():height() - _dy,
32282             z0 = _dz<0?-_dz:0,
32283             z1 = _dz<0?depth():depth() - _dz;
32284           const longT
32285             wh = (longT)width()*height(),
32286             whd = (longT)width()*height()*depth(),
32287             offset = _dz*wh + _dy*width() + _dx;
32288           for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
32289             for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) {
32290               for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
32291                 Tfloat diff;
32292                 switch (_spectrum) {
32293                 case 1 :
32294                   diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd));
32295                   break;
32296                 case 2 :
32297                   if (is_L2_norm)
32298                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32299                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
32300                   else
32301                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32302                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
32303                   break;
32304                 case 3 :
32305                   if (is_L2_norm)
32306                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32307                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
32308                       cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
32309                   else
32310                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32311                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
32312                       cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
32313                   break;
32314                 case 4 :
32315                   if (is_L2_norm)
32316                     diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32317                       cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
32318                       cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
32319                       cimg::sqr((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
32320                   else
32321                     diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
32322                       cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
32323                       cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
32324                       cimg::abs((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
32325                   break;
32326                 default :
32327                   diff = 0;
32328                   if (is_L2_norm)
32329                     cimg_forC(*this,c)
32330                       diff+=cimg::sqr((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
32331                   else
32332                     cimg_forC(*this,c)
32333                       diff+=cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
32334                 }
32335 
32336                 if (diff<=_tolerance) {
32337                   const longT q = p + offset;
32338                   ulongT xk, yk;
32339                   for (xk = (ulongT)(p<q?q:p), yk = (ulongT)(p<q?p:q); xk!=yk && res[xk]!=xk; ) {
32340                     xk = res[xk]; if (xk<yk) cimg::swap(xk,yk);
32341                   }
32342                   if (xk!=yk) res[xk] = (ulongT)yk;
32343                   for (ulongT _p = (ulongT)p; _p!=yk; ) {
32344                     const ulongT h = res[_p];
32345                     res[_p] = (ulongT)yk;
32346                     _p = h;
32347                   }
32348                   for (ulongT _q = (ulongT)q; _q!=yk; ) {
32349                     const ulongT h = res[_q];
32350                     res[_q] = (ulongT)yk;
32351                     _q = h;
32352                   }
32353                 }
32354               }
32355             }
32356           }
32357         }
32358       }
32359 
32360       // Resolve equivalences.
32361       ulongT counter = 0;
32362       ptr = res.data();
32363       cimg_foroff(res,p) { *ptr = *ptr==p?counter++:res[*ptr]; ++ptr; }
32364       return res;
32365     }
32366 
32367     // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
32368     CImg<T>& _system_strescape() {
32369 #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
32370       move_to(list); \
32371       CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break
32372       CImgList<T> list;
32373       const T *ptrs = _data;
32374       cimg_for(*this,p,T) switch ((int)*p) {
32375         cimg_system_strescape('\\',"\\\\");
32376         cimg_system_strescape('\"',"\\\"");
32377         cimg_system_strescape('!',"\"\\!\"");
32378         cimg_system_strescape('`',"\\`");
32379         cimg_system_strescape('$',"\\$");
32380       }
32381       if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
32382       return (list>'x').move_to(*this);
32383     }
32384 
32385     //@}
32386     //---------------------------------
32387     //
32388     //! \name Color Base Management
32389     //@{
32390     //---------------------------------
32391 
32392     //! Return colormap \e "default", containing 256 colors entries in RGB.
32393     /**
32394        \return The following \c 256x1x1x3 colormap is returned:
32395        \image html ref_colormap_default.jpg
32396     **/
32397     static const CImg<Tuchar>& default_LUT256() {
32398       static CImg<Tuchar> colormap;
32399       cimg::mutex(8);
32400       if (!colormap) {
32401         colormap.assign(1,256,1,3);
32402         for (unsigned int index = 0, r = 16; r<256; r+=32)
32403           for (unsigned int g = 16; g<256; g+=32)
32404             for (unsigned int b = 32; b<256; b+=64) {
32405               colormap(0,index,0) = (Tuchar)r;
32406               colormap(0,index,1) = (Tuchar)g;
32407               colormap(0,index++,2) = (Tuchar)b;
32408             }
32409       }
32410       cimg::mutex(8,0);
32411       return colormap;
32412     }
32413 
32414     //! Return colormap \e "HSV", containing 256 colors entries in RGB.
32415     /**
32416        \return The following \c 256x1x1x3 colormap is returned:
32417        \image html ref_colormap_hsv.jpg
32418     **/
32419     static const CImg<Tuchar>& HSV_LUT256() {
32420       static CImg<Tuchar> colormap;
32421       cimg::mutex(8);
32422       if (!colormap) {
32423         CImg<Tint> tmp(1,256,1,3,1);
32424         tmp.get_shared_channel(0).sequence(0,359);
32425         colormap = tmp.HSVtoRGB();
32426       }
32427       cimg::mutex(8,0);
32428       return colormap;
32429     }
32430 
32431     //! Return colormap \e "lines", containing 256 colors entries in RGB.
32432     /**
32433        \return The following \c 256x1x1x3 colormap is returned:
32434        \image html ref_colormap_lines.jpg
32435     **/
32436     static const CImg<Tuchar>& lines_LUT256() {
32437       static const unsigned char pal[] = {
32438         0,255,255,0,0,28,125,125,235,210,186,182,36,0,125,255,
32439         53,32,255,210,89,186,65,45,125,210,210,97,130,194,0,125,
32440         206,53,190,89,255,146,20,190,154,73,255,36,130,215,0,138,
32441         101,210,61,194,206,0,77,45,255,154,174,0,190,239,89,125,
32442         16,36,158,223,117,0,97,69,223,255,40,239,0,0,255,0,
32443         97,170,93,255,138,40,117,210,0,170,53,158,186,255,0,121,
32444         227,121,186,40,20,190,89,255,77,57,130,142,255,73,186,85,
32445         210,8,32,166,243,130,210,40,255,45,61,142,223,49,121,255,
32446         20,162,158,73,89,255,53,138,210,190,57,235,36,73,255,49,
32447         210,0,210,85,57,97,255,121,85,174,40,255,162,178,0,121,
32448         166,125,53,146,166,255,97,121,65,89,235,231,12,170,36,190,
32449         85,255,166,97,198,77,20,146,109,166,255,28,40,202,121,81,
32450         247,0,210,255,49,0,65,255,36,166,93,77,255,85,251,0,
32451         170,178,0,182,255,0,162,16,154,142,162,223,223,0,0,81,
32452         215,4,215,162,215,125,77,206,121,36,125,231,101,16,255,121,
32453         0,57,190,215,65,125,89,142,255,101,73,53,146,223,125,125,
32454         0,255,0,255,0,206,93,138,49,255,0,202,154,85,45,219,
32455         251,53,0,255,40,130,219,158,16,117,186,130,202,49,65,239,
32456         89,202,49,28,247,134,150,0,255,117,202,4,215,81,186,57,
32457         202,89,73,210,40,93,45,251,206,28,223,142,40,134,162,125,
32458         32,247,97,170,0,255,57,134,73,247,162,0,251,40,142,142,
32459         8,166,206,81,154,194,93,89,125,243,28,109,227,0,190,65,
32460         194,186,0,255,53,45,109,186,186,0,255,130,49,170,69,210,
32461         154,0,109,227,45,255,125,105,81,81,255,0,219,134,170,85,
32462         146,28,170,89,223,97,8,210,255,158,49,40,125,174,174,125,
32463         0,227,166,28,219,130,0,93,239,0,85,255,81,178,125,49,
32464         89,255,53,206,73,113,146,255,0,150,36,219,162,0,210,125,
32465         69,134,255,85,40,89,235,49,215,121,0,206,36,223,174,69,
32466         40,182,178,130,69,45,255,210,85,77,215,0,231,146,0,194,
32467         125,174,0,255,40,89,121,206,57,0,206,170,231,150,81,0,
32468         125,255,4,174,4,190,121,255,4,166,109,130,49,239,170,93,
32469         16,174,210,0,255,16,105,158,93,255,0,125,0,255,158,85,
32470         0,255,0,0,255,170,166,61,121,28,198,215,45,243,61,97,
32471         255,53,81,130,109,255,8,117,235,121,40,178,174,0,182,49,
32472         162,121,255,69,206,0,219,125,0,101,255,239,121,32,210,130,
32473         36,231,32,125,81,142,215,158,4,178,255,0,40,251,125,125,
32474         219,89,130,0,166,255,24,65,194,125,255,125,77,125,93,125,
32475         202,24,138,174,178,32,255,85,194,40,85,36,174,174,125,210,
32476         85,255,53,16,93,206,40,130,170,202,93,255,0,24,117,255,
32477         97,113,105,81,255,186,194,57,69,206,57,53,223,190,4,255,
32478         85,97,130,255,85,0,125,223,85,219,0,215,146,77,40,239,
32479         89,36,142,154,227,0,255,85,162,0,162,0,235,178,45,166,
32480         0,247,255,20,69,210,89,142,53,255,40,146,166,255,69,0,
32481         174,154,142,130,162,0,215,255,0,89,40,255,166,61,146,69,
32482         162,40,255,32,121,255,117,178,0,186,206,0,57,215,215,81,
32483         158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97,
32484         57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4,
32485         73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 };
32486       static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
32487       return colormap;
32488     }
32489 
32490     //! Return colormap \e "hot", containing 256 colors entries in RGB.
32491     /**
32492        \return The following \c 256x1x1x3 colormap is returned:
32493        \image html ref_colormap_hot.jpg
32494     **/
32495     static const CImg<Tuchar>& hot_LUT256() {
32496       static CImg<Tuchar> colormap;
32497       cimg::mutex(8);
32498       if (!colormap) {
32499         colormap.assign(1,4,1,3,(T)0);
32500         colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
32501         colormap.resize(1,256,1,3,3);
32502       }
32503       cimg::mutex(8,0);
32504       return colormap;
32505     }
32506 
32507     //! Return colormap \e "cool", containing 256 colors entries in RGB.
32508     /**
32509        \return The following \c 256x1x1x3 colormap is returned:
32510        \image html ref_colormap_cool.jpg
32511     **/
32512     static const CImg<Tuchar>& cool_LUT256() {
32513       static CImg<Tuchar> colormap;
32514       cimg::mutex(8);
32515       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);
32516       cimg::mutex(8,0);
32517       return colormap;
32518     }
32519 
32520     //! Return colormap \e "jet", containing 256 colors entries in RGB.
32521     /**
32522        \return The following \c 256x1x1x3 colormap is returned:
32523        \image html ref_colormap_jet.jpg
32524     **/
32525     static const CImg<Tuchar>& jet_LUT256() {
32526       static CImg<Tuchar> colormap;
32527       cimg::mutex(8);
32528       if (!colormap) {
32529         colormap.assign(1,4,1,3,(T)0);
32530         colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
32531         colormap.resize(1,256,1,3,3);
32532       }
32533       cimg::mutex(8,0);
32534       return colormap;
32535     }
32536 
32537     //! Return colormap \e "flag", containing 256 colors entries in RGB.
32538     /**
32539        \return The following \c 256x1x1x3 colormap is returned:
32540        \image html ref_colormap_flag.jpg
32541     **/
32542     static const CImg<Tuchar>& flag_LUT256() {
32543       static CImg<Tuchar> colormap;
32544       cimg::mutex(8);
32545       if (!colormap) {
32546         colormap.assign(1,4,1,3,(T)0);
32547         colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
32548         colormap.resize(1,256,1,3,0,2);
32549       }
32550       cimg::mutex(8,0);
32551       return colormap;
32552     }
32553 
32554     //! Return colormap \e "cube", containing 256 colors entries in RGB.
32555     /**
32556        \return The following \c 256x1x1x3 colormap is returned:
32557        \image html ref_colormap_cube.jpg
32558     **/
32559     static const CImg<Tuchar>& cube_LUT256() {
32560       static CImg<Tuchar> colormap;
32561       cimg::mutex(8);
32562       if (!colormap) {
32563         colormap.assign(1,8,1,3,(T)0);
32564         colormap[1] = colormap[3] = colormap[5] = colormap[7] =
32565           colormap[10] = colormap[11] = colormap[12] = colormap[13] =
32566           colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
32567         colormap.resize(1,256,1,3,3);
32568       }
32569       cimg::mutex(8,0);
32570       return colormap;
32571     }
32572 
32573     //! Convert pixel values from sRGB to RGB color spaces.
32574     CImg<T>& sRGBtoRGB() {
32575       if (is_empty()) return *this;
32576       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
32577       cimg_rofoff(*this,off) {
32578         const Tfloat
32579           sval = (Tfloat)_data[off]/255,
32580           val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f));
32581         _data[off] = (T)cimg::cut(val*255,0,255);
32582       }
32583       return *this;
32584     }
32585 
32586     //! Convert pixel values from sRGB to RGB color spaces \newinstance.
32587     CImg<Tfloat> get_sRGBtoRGB() const {
32588       return CImg<Tfloat>(*this,false).sRGBtoRGB();
32589     }
32590 
32591     //! Convert pixel values from RGB to sRGB color spaces.
32592     CImg<T>& RGBtosRGB() {
32593       if (is_empty()) return *this;
32594       cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
32595       cimg_rofoff(*this,off) {
32596         const Tfloat
32597           val = (Tfloat)_data[off]/255,
32598           sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f);
32599         _data[off] = (T)cimg::cut(sval*255,0,255);
32600       }
32601       return *this;
32602     }
32603 
32604     //! Convert pixel values from RGB to sRGB color spaces \newinstance.
32605     CImg<Tfloat> get_RGBtosRGB() const {
32606       return CImg<Tfloat>(*this,false).RGBtosRGB();
32607     }
32608 
32609     //! Convert pixel values from RGB to HSI color spaces.
32610     CImg<T>& RGBtoHSI() {
32611       if (_spectrum!=3)
32612         throw CImgInstanceException(_cimg_instance
32613                                     "RGBtoHSI(): Instance is not a RGB image.",
32614                                     cimg_instance);
32615 
32616       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32617       const longT whd = (longT)width()*height()*depth();
32618       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
32619       for (longT N = 0; N<whd; ++N) {
32620         const Tfloat
32621           R = (Tfloat)p1[N],
32622           G = (Tfloat)p2[N],
32623           B = (Tfloat)p3[N],
32624           m = cimg::min(R,G,B),
32625           M = cimg::max(R,G,B),
32626           C = M - m,
32627           sum = R + G + B,
32628           H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
32629           S = sum<=0?0:1 - 3*m/sum,
32630           I = sum/(3*255);
32631         p1[N] = (T)H;
32632         p2[N] = (T)S;
32633         p3[N] = (T)I;
32634       }
32635       return *this;
32636     }
32637 
32638     //! Convert pixel values from RGB to HSI color spaces \newinstance.
32639     CImg<Tfloat> get_RGBtoHSI() const {
32640       return CImg<Tfloat>(*this,false).RGBtoHSI();
32641     }
32642 
32643     //! Convert pixel values from HSI to RGB color spaces.
32644     CImg<T>& HSItoRGB() {
32645       if (_spectrum!=3)
32646         throw CImgInstanceException(_cimg_instance
32647                                     "HSItoRGB(): Instance is not a HSI image.",
32648                                     cimg_instance);
32649 
32650       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32651       const longT whd = (longT)width()*height()*depth();
32652       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
32653       for (longT N = 0; N<whd; ++N) {
32654         const Tfloat
32655           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
32656           S = (Tfloat)p2[N],
32657           I = (Tfloat)p3[N],
32658           Z = 1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1),
32659           C = I*S/(1 + Z),
32660           X = C*Z,
32661           m = I*(1 - S)/3;
32662         Tfloat R, G, B;
32663         switch ((int)H) {
32664         case 0 : R = C; G = X; B = 0; break;
32665         case 1 : R = X; G = C; B = 0; break;
32666         case 2 : R = 0; G = C; B = X; break;
32667         case 3 : R = 0; G = X; B = C; break;
32668         case 4 : R = X; G = 0; B = C; break;
32669         default : R = C; G = 0; B = X;
32670         }
32671         p1[N] = (T)((R + m)*3*255);
32672         p2[N] = (T)((G + m)*3*255);
32673         p3[N] = (T)((B + m)*3*255);
32674       }
32675       return *this;
32676     }
32677 
32678     //! Convert pixel values from HSI to RGB color spaces \newinstance.
32679     CImg<Tfloat> get_HSItoRGB() const {
32680       return CImg< Tuchar>(*this,false).HSItoRGB();
32681     }
32682 
32683     //! Convert pixel values from RGB to HSL color spaces.
32684     CImg<T>& RGBtoHSL() {
32685       if (_spectrum!=3)
32686         throw CImgInstanceException(_cimg_instance
32687                                     "RGBtoHSL(): Instance is not a RGB image.",
32688                                     cimg_instance);
32689 
32690       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32691       const longT whd = (longT)width()*height()*depth();
32692       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
32693       for (longT N = 0; N<whd; ++N) {
32694         const Tfloat
32695           R = (Tfloat)p1[N],
32696           G = (Tfloat)p2[N],
32697           B = (Tfloat)p3[N],
32698           m = cimg::min(R,G,B),
32699           M = cimg::max(R,G,B),
32700           C = M - m,
32701           H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
32702           L = 0.5f*(m + M)/255,
32703           S = L==1 || L==0?0:C/(1 - cimg::abs(2*L - 1))/255;
32704         p1[N] = (T)H;
32705         p2[N] = (T)S;
32706         p3[N] = (T)L;
32707       }
32708       return *this;
32709     }
32710 
32711     //! Convert pixel values from RGB to HSL color spaces \newinstance.
32712     CImg<Tfloat> get_RGBtoHSL() const {
32713       return CImg<Tfloat>(*this,false).RGBtoHSL();
32714     }
32715 
32716     //! Convert pixel values from HSL to RGB color spaces.
32717     CImg<T>& HSLtoRGB() {
32718       if (_spectrum!=3)
32719         throw CImgInstanceException(_cimg_instance
32720                                     "HSLtoRGB(): Instance is not a HSL image.",
32721                                     cimg_instance);
32722 
32723       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32724       const longT whd = (longT)width()*height()*depth();
32725       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
32726       for (longT N = 0; N<whd; ++N) {
32727         const Tfloat
32728           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
32729           S = (Tfloat)p2[N],
32730           L = (Tfloat)p3[N],
32731           C = (1 - cimg::abs(2*L - 1))*S,
32732           X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
32733           m = L - C/2;
32734         Tfloat R, G, B;
32735         switch ((int)H) {
32736         case 0 : R = C; G = X; B = 0; break;
32737         case 1 : R = X; G = C; B = 0; break;
32738         case 2 : R = 0; G = C; B = X; break;
32739         case 3 : R = 0; G = X; B = C; break;
32740         case 4 : R = X; G = 0; B = C; break;
32741         default : R = C; G = 0; B = X;
32742         }
32743         p1[N] = (T)((R + m)*255);
32744         p2[N] = (T)((G + m)*255);
32745         p3[N] = (T)((B + m)*255);
32746       }
32747       return *this;
32748     }
32749 
32750     //! Convert pixel values from HSL to RGB color spaces \newinstance.
32751     CImg<Tuchar> get_HSLtoRGB() const {
32752       return CImg<Tuchar>(*this,false).HSLtoRGB();
32753     }
32754 
32755     //! Convert pixel values from RGB to HSV color spaces.
32756     CImg<T>& RGBtoHSV() {
32757       if (_spectrum!=3)
32758         throw CImgInstanceException(_cimg_instance
32759                                     "RGBtoHSV(): Instance is not a RGB image.",
32760                                     cimg_instance);
32761 
32762       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32763       const longT whd = (longT)width()*height()*depth();
32764       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
32765       for (longT N = 0; N<whd; ++N) {
32766         const Tfloat
32767           R = (Tfloat)p1[N],
32768           G = (Tfloat)p2[N],
32769           B = (Tfloat)p3[N],
32770           M = cimg::max(R,G,B),
32771           C = M - cimg::min(R,G,B),
32772           H = 60*(C==0?0:M==R?cimg::mod((G-B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
32773           S = M<=0?0:C/M;
32774         p1[N] = (T)H;
32775         p2[N] = (T)S;
32776         p3[N] = (T)(M/255);
32777       }
32778       return *this;
32779     }
32780 
32781     //! Convert pixel values from RGB to HSV color spaces \newinstance.
32782     CImg<Tfloat> get_RGBtoHSV() const {
32783       return CImg<Tfloat>(*this,false).RGBtoHSV();
32784     }
32785 
32786     //! Convert pixel values from HSV to RGB color spaces.
32787     CImg<T>& HSVtoRGB() {
32788       if (_spectrum!=3)
32789         throw CImgInstanceException(_cimg_instance
32790                                     "HSVtoRGB(): Instance is not a HSV image.",
32791                                     cimg_instance);
32792 
32793       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32794       const longT whd = (longT)width()*height()*depth();
32795       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
32796       for (longT N = 0; N<whd; ++N) {
32797         Tfloat
32798           H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
32799           S = (Tfloat)p2[N],
32800           V = (Tfloat)p3[N],
32801           C = V*S,
32802           X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
32803           m = V - C;
32804         Tfloat R, G, B;
32805         switch ((int)H) {
32806         case 0 : R = C; G = X; B = 0; break;
32807         case 1 : R = X; G = C; B = 0; break;
32808         case 2 : R = 0; G = C; B = X; break;
32809         case 3 : R = 0; G = X; B = C; break;
32810         case 4 : R = X; G = 0; B = C; break;
32811         default : R = C; G = 0; B = X;
32812         }
32813         p1[N] = (T)((R + m)*255);
32814         p2[N] = (T)((G + m)*255);
32815         p3[N] = (T)((B + m)*255);
32816       }
32817       return *this;
32818     }
32819 
32820     //! Convert pixel values from HSV to RGB color spaces \newinstance.
32821     CImg<Tuchar> get_HSVtoRGB() const {
32822       return CImg<Tuchar>(*this,false).HSVtoRGB();
32823     }
32824 
32825     //! Convert pixel values from RGB to YCbCr color spaces.
32826     CImg<T>& RGBtoYCbCr() {
32827       if (_spectrum!=3)
32828         throw CImgInstanceException(_cimg_instance
32829                                     "RGBtoYCbCr(): Instance is not a RGB image.",
32830                                     cimg_instance);
32831 
32832       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32833       const longT whd = (longT)width()*height()*depth();
32834       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
32835       for (longT N = 0; N<whd; ++N) {
32836         const Tfloat
32837           R = (Tfloat)p1[N],
32838           G = (Tfloat)p2[N],
32839           B = (Tfloat)p3[N],
32840           Y = (66*R + 129*G + 25*B + 128)/256 + 16,
32841           Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
32842           Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
32843         p1[N] = (T)cimg::cut(Y,0,255),
32844         p2[N] = (T)cimg::cut(Cb,0,255),
32845         p3[N] = (T)cimg::cut(Cr,0,255);
32846       }
32847       return *this;
32848     }
32849 
32850     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
32851     CImg<Tuchar> get_RGBtoYCbCr() const {
32852       return CImg<Tuchar>(*this,false).RGBtoYCbCr();
32853     }
32854 
32855     //! Convert pixel values from RGB to YCbCr color spaces.
32856     CImg<T>& YCbCrtoRGB() {
32857       if (_spectrum!=3)
32858         throw CImgInstanceException(_cimg_instance
32859                                     "YCbCrtoRGB(): Instance is not a YCbCr image.",
32860                                     cimg_instance);
32861 
32862       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32863       const longT whd = (longT)width()*height()*depth();
32864       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
32865       for (longT N = 0; N<whd; ++N) {
32866         const Tfloat
32867           Y = (Tfloat)p1[N] - 16,
32868           Cb = (Tfloat)p2[N] - 128,
32869           Cr = (Tfloat)p3[N] - 128,
32870           R = (298*Y + 409*Cr + 128)/256,
32871           G = (298*Y - 100*Cb - 208*Cr + 128)/256,
32872           B = (298*Y + 516*Cb + 128)/256;
32873         p1[N] = (T)cimg::cut(R,0,255),
32874         p2[N] = (T)cimg::cut(G,0,255),
32875         p3[N] = (T)cimg::cut(B,0,255);
32876       }
32877       return *this;
32878     }
32879 
32880     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
32881     CImg<Tuchar> get_YCbCrtoRGB() const {
32882       return CImg<Tuchar>(*this,false).YCbCrtoRGB();
32883     }
32884 
32885     //! Convert pixel values from RGB to YUV color spaces.
32886     CImg<T>& RGBtoYUV() {
32887       if (_spectrum!=3)
32888         throw CImgInstanceException(_cimg_instance
32889                                     "RGBtoYUV(): Instance is not a RGB image.",
32890                                     cimg_instance);
32891 
32892       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32893       const longT whd = (longT)width()*height()*depth();
32894       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
32895       for (longT N = 0; N<whd; ++N) {
32896         const Tfloat
32897           R = (Tfloat)p1[N]/255,
32898           G = (Tfloat)p2[N]/255,
32899           B = (Tfloat)p3[N]/255,
32900           Y = 0.299f*R + 0.587f*G + 0.114f*B;
32901         p1[N] = (T)Y;
32902         p2[N] = (T)(0.492f*(B - Y));
32903         p3[N] = (T)(0.877*(R - Y));
32904       }
32905       return *this;
32906     }
32907 
32908     //! Convert pixel values from RGB to YUV color spaces \newinstance.
32909     CImg<Tfloat> get_RGBtoYUV() const {
32910       return CImg<Tfloat>(*this,false).RGBtoYUV();
32911     }
32912 
32913     //! Convert pixel values from YUV to RGB color spaces.
32914     CImg<T>& YUVtoRGB() {
32915       if (_spectrum!=3)
32916         throw CImgInstanceException(_cimg_instance
32917                                     "YUVtoRGB(): Instance is not a YUV image.",
32918                                     cimg_instance);
32919 
32920       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32921       const longT whd = (longT)width()*height()*depth();
32922       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
32923       for (longT N = 0; N<whd; ++N) {
32924         const Tfloat
32925           Y = (Tfloat)p1[N],
32926           U = (Tfloat)p2[N],
32927           V = (Tfloat)p3[N],
32928           R = (Y + 1.140f*V)*255,
32929           G = (Y - 0.395f*U - 0.581f*V)*255,
32930           B = (Y + 2.032f*U)*255;
32931         p1[N] = (T)cimg::cut(R,0,255),
32932         p2[N] = (T)cimg::cut(G,0,255),
32933         p3[N] = (T)cimg::cut(B,0,255);
32934       }
32935       return *this;
32936     }
32937 
32938     //! Convert pixel values from YUV to RGB color spaces \newinstance.
32939     CImg<Tuchar> get_YUVtoRGB() const {
32940       return CImg< Tuchar>(*this,false).YUVtoRGB();
32941     }
32942 
32943     //! Convert pixel values from RGB to CMY color spaces.
32944     CImg<T>& RGBtoCMY() {
32945       if (_spectrum!=3)
32946         throw CImgInstanceException(_cimg_instance
32947                                     "RGBtoCMY(): Instance is not a RGB image.",
32948                                     cimg_instance);
32949 
32950       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32951       const longT whd = (longT)width()*height()*depth();
32952       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
32953       for (longT N = 0; N<whd; ++N) {
32954         const Tfloat
32955           R = (Tfloat)p1[N],
32956           G = (Tfloat)p2[N],
32957           B = (Tfloat)p3[N],
32958           C = 255 - R,
32959           M = 255 - G,
32960           Y = 255 - B;
32961         p1[N] = (T)cimg::cut(C,0,255),
32962         p2[N] = (T)cimg::cut(M,0,255),
32963         p3[N] = (T)cimg::cut(Y,0,255);
32964       }
32965       return *this;
32966     }
32967 
32968     //! Convert pixel values from RGB to CMY color spaces \newinstance.
32969     CImg<Tuchar> get_RGBtoCMY() const {
32970       return CImg<Tfloat>(*this,false).RGBtoCMY();
32971     }
32972 
32973     //! Convert pixel values from CMY to RGB color spaces.
32974     CImg<T>& CMYtoRGB() {
32975       if (_spectrum!=3)
32976         throw CImgInstanceException(_cimg_instance
32977                                     "CMYtoRGB(): Instance is not a CMY image.",
32978                                     cimg_instance);
32979 
32980       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
32981       const longT whd = (longT)width()*height()*depth();
32982       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
32983       for (longT N = 0; N<whd; ++N) {
32984         const Tfloat
32985           C = (Tfloat)p1[N],
32986           M = (Tfloat)p2[N],
32987           Y = (Tfloat)p3[N],
32988           R = 255 - C,
32989           G = 255 - M,
32990           B = 255 - Y;
32991         p1[N] = (T)cimg::cut(R,0,255),
32992         p2[N] = (T)cimg::cut(G,0,255),
32993         p3[N] = (T)cimg::cut(B,0,255);
32994       }
32995       return *this;
32996     }
32997 
32998     //! Convert pixel values from CMY to RGB color spaces \newinstance.
32999     CImg<Tuchar> get_CMYtoRGB() const {
33000       return CImg<Tuchar>(*this,false).CMYtoRGB();
33001     }
33002 
33003     //! Convert pixel values from CMY to CMYK color spaces.
33004     CImg<T>& CMYtoCMYK() {
33005       return get_CMYtoCMYK().move_to(*this);
33006     }
33007 
33008     //! Convert pixel values from CMY to CMYK color spaces \newinstance.
33009     CImg<Tuchar> get_CMYtoCMYK() const {
33010       if (_spectrum!=3)
33011         throw CImgInstanceException(_cimg_instance
33012                                     "CMYtoCMYK(): Instance is not a CMY image.",
33013                                     cimg_instance);
33014 
33015       CImg<Tfloat> res(_width,_height,_depth,4);
33016       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
33017       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);
33018       const longT whd = (longT)width()*height()*depth();
33019       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
33020       for (longT N = 0; N<whd; ++N) {
33021         Tfloat
33022           C = (Tfloat)ps1[N],
33023           M = (Tfloat)ps2[N],
33024           Y = (Tfloat)ps3[N],
33025           K = cimg::min(C,M,Y);
33026         if (K>=255) C = M = Y = 0;
33027         else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
33028         pd1[N] = (Tfloat)cimg::cut(C,0,255),
33029         pd2[N] = (Tfloat)cimg::cut(M,0,255),
33030         pd3[N] = (Tfloat)cimg::cut(Y,0,255),
33031         pd4[N] = (Tfloat)cimg::cut(K,0,255);
33032       }
33033       return res;
33034     }
33035 
33036     //! Convert pixel values from CMYK to CMY color spaces.
33037     CImg<T>& CMYKtoCMY() {
33038       return get_CMYKtoCMY().move_to(*this);
33039     }
33040 
33041     //! Convert pixel values from CMYK to CMY color spaces \newinstance.
33042     CImg<Tfloat> get_CMYKtoCMY() const {
33043       if (_spectrum!=4)
33044         throw CImgInstanceException(_cimg_instance
33045                                     "CMYKtoCMY(): Instance is not a CMYK image.",
33046                                     cimg_instance);
33047 
33048       CImg<Tfloat> res(_width,_height,_depth,3);
33049       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);
33050       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
33051       const longT whd = (longT)width()*height()*depth();
33052       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
33053       for (longT N = 0; N<whd; ++N) {
33054         const Tfloat
33055           C = (Tfloat)ps1[N],
33056           M = (Tfloat)ps2[N],
33057           Y = (Tfloat)ps3[N],
33058           K = (Tfloat)ps4[N],
33059           K1 = 1 - K/255,
33060           nC = C*K1 + K,
33061           nM = M*K1 + K,
33062           nY = Y*K1 + K;
33063         pd1[N] = (Tfloat)cimg::cut(nC,0,255),
33064         pd2[N] = (Tfloat)cimg::cut(nM,0,255),
33065         pd3[N] = (Tfloat)cimg::cut(nY,0,255);
33066       }
33067       return res;
33068     }
33069 
33070     //! Convert pixel values from RGB to XYZ color spaces.
33071     /**
33072        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
33073     **/
33074     CImg<T>& RGBtoXYZ(const bool use_D65=true) {
33075       if (_spectrum!=3)
33076         throw CImgInstanceException(_cimg_instance
33077                                     "RGBtoXYZ(): Instance is not a RGB image.",
33078                                     cimg_instance);
33079 
33080       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33081       const longT whd = (longT)width()*height()*depth();
33082       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33083       for (longT N = 0; N<whd; ++N) {
33084         const Tfloat
33085           R = (Tfloat)p1[N]/255,
33086           G = (Tfloat)p2[N]/255,
33087           B = (Tfloat)p3[N]/255;
33088         if (use_D65) { // D65
33089           p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B);
33090           p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B);
33091           p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B);
33092         } else { // D50
33093           p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B);
33094           p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B);
33095           p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B);
33096         }
33097       }
33098       return *this;
33099     }
33100 
33101     //! Convert pixel values from RGB to XYZ color spaces \newinstance.
33102     CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const {
33103       return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65);
33104     }
33105 
33106     //! Convert pixel values from XYZ to RGB color spaces.
33107     /**
33108        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
33109     **/
33110     CImg<T>& XYZtoRGB(const bool use_D65=true) {
33111       if (_spectrum!=3)
33112         throw CImgInstanceException(_cimg_instance
33113                                     "XYZtoRGB(): Instance is not a XYZ image.",
33114                                     cimg_instance);
33115 
33116       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33117       const longT whd = (longT)width()*height()*depth();
33118       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
33119       for (longT N = 0; N<whd; ++N) {
33120         const Tfloat
33121           X = (Tfloat)p1[N]*255,
33122           Y = (Tfloat)p2[N]*255,
33123           Z = (Tfloat)p3[N]*255;
33124         if (use_D65) {
33125           p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255);
33126           p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255);
33127           p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255);
33128         } else {
33129           p1[N] = (T)cimg::cut(3.134274799724*X  - 1.617275708956*Y - 0.490724283042*Z,0,255);
33130           p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255);
33131           p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255);
33132         }
33133       }
33134       return *this;
33135     }
33136 
33137     //! Convert pixel values from XYZ to RGB color spaces \newinstance.
33138     CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const {
33139       return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65);
33140     }
33141 
33142     //! Convert pixel values from XYZ to Lab color spaces.
33143     CImg<T>& XYZtoLab(const bool use_D65=true) {
33144 #define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116)
33145 
33146       if (_spectrum!=3)
33147         throw CImgInstanceException(_cimg_instance
33148                                     "XYZtoLab(): Instance is not a XYZ image.",
33149                                     cimg_instance);
33150       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
33151       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33152       const longT whd = (longT)width()*height()*depth();
33153       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
33154       for (longT N = 0; N<whd; ++N) {
33155         const Tfloat
33156           X = (Tfloat)(p1[N]/white[0]),
33157           Y = (Tfloat)(p2[N]/white[1]),
33158           Z = (Tfloat)(p3[N]/white[2]),
33159           fX = (Tfloat)_cimg_Labf(X),
33160           fY = (Tfloat)_cimg_Labf(Y),
33161           fZ = (Tfloat)_cimg_Labf(Z);
33162         p1[N] = (T)cimg::cut(116*fY - 16,0,100);
33163         p2[N] = (T)(500*(fX - fY));
33164         p3[N] = (T)(200*(fY - fZ));
33165       }
33166       return *this;
33167     }
33168 
33169     //! Convert pixel values from XYZ to Lab color spaces \newinstance.
33170     CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const {
33171       return CImg<Tfloat>(*this,false).XYZtoLab(use_D65);
33172     }
33173 
33174     //! Convert pixel values from Lab to XYZ color spaces.
33175     CImg<T>& LabtoXYZ(const bool use_D65=true) {
33176       if (_spectrum!=3)
33177         throw CImgInstanceException(_cimg_instance
33178                                     "LabtoXYZ(): Instance is not a Lab image.",
33179                                     cimg_instance);
33180       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
33181       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33182       const longT whd = (longT)width()*height()*depth();
33183       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
33184       for (longT N = 0; N<whd; ++N) {
33185         const Tfloat
33186           L = (Tfloat)p1[N],
33187           a = (Tfloat)p2[N],
33188           b = (Tfloat)p3[N],
33189           cY = (L + 16)/116,
33190           cZ = cY - b/200,
33191           cX = a/500 + cY,
33192           X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389),
33193           Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389),
33194           Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389);
33195         p1[N] = (T)(X*white[0]);
33196         p2[N] = (T)(Y*white[1]);
33197         p3[N] = (T)(Z*white[2]);
33198       }
33199       return *this;
33200     }
33201 
33202     //! Convert pixel values from Lab to XYZ color spaces \newinstance.
33203     CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const {
33204       return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65);
33205     }
33206 
33207     //! Convert pixel values from XYZ to xyY color spaces.
33208     CImg<T>& XYZtoxyY() {
33209       if (_spectrum!=3)
33210         throw CImgInstanceException(_cimg_instance
33211                                     "XYZtoxyY(): Instance is not a XYZ image.",
33212                                     cimg_instance);
33213 
33214       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33215       const longT whd = (longT)width()*height()*depth();
33216       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
33217       for (longT N = 0; N<whd; ++N) {
33218         const Tfloat
33219           X = (Tfloat)p1[N],
33220           Y = (Tfloat)p2[N],
33221           Z = (Tfloat)p3[N],
33222           sum = X + Y + Z,
33223           nsum = sum>0?sum:1;
33224         p1[N] = (T)(X/nsum);
33225         p2[N] = (T)(Y/nsum);
33226         p3[N] = (T)Y;
33227       }
33228       return *this;
33229     }
33230 
33231     //! Convert pixel values from XYZ to xyY color spaces \newinstance.
33232     CImg<Tfloat> get_XYZtoxyY() const {
33233       return CImg<Tfloat>(*this,false).XYZtoxyY();
33234     }
33235 
33236     //! Convert pixel values from xyY pixels to XYZ color spaces.
33237     CImg<T>& xyYtoXYZ() {
33238       if (_spectrum!=3)
33239         throw CImgInstanceException(_cimg_instance
33240                                     "xyYtoXYZ(): Instance is not a xyY image.",
33241                                     cimg_instance);
33242 
33243       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
33244       const longT whd = (longT)width()*height()*depth();
33245       cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
33246       for (longT N = 0; N<whd; ++N) {
33247         const Tfloat
33248          px = (Tfloat)p1[N],
33249          py = (Tfloat)p2[N],
33250          Y = (Tfloat)p3[N],
33251          ny = py>0?py:1;
33252         p1[N] = (T)(px*Y/ny);
33253         p2[N] = (T)Y;
33254         p3[N] = (T)((1 - px - py)*Y/ny);
33255       }
33256       return *this;
33257     }
33258 
33259     //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance.
33260     CImg<Tfloat> get_xyYtoXYZ() const {
33261       return CImg<Tfloat>(*this,false).xyYtoXYZ();
33262     }
33263 
33264     //! Convert pixel values from RGB to Lab color spaces.
33265     CImg<T>& RGBtoLab(const bool use_D65=true) {
33266       return RGBtoXYZ(use_D65).XYZtoLab(use_D65);
33267     }
33268 
33269     //! Convert pixel values from RGB to Lab color spaces \newinstance.
33270     CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const {
33271       return CImg<Tfloat>(*this,false).RGBtoLab(use_D65);
33272     }
33273 
33274     //! Convert pixel values from Lab to RGB color spaces.
33275     CImg<T>& LabtoRGB(const bool use_D65=true) {
33276       return LabtoXYZ().XYZtoRGB(use_D65);
33277     }
33278 
33279     //! Convert pixel values from Lab to RGB color spaces \newinstance.
33280     CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const {
33281       return CImg<Tuchar>(*this,false).LabtoRGB(use_D65);
33282     }
33283 
33284     //! Convert pixel values from RGB to xyY color spaces.
33285     CImg<T>& RGBtoxyY(const bool use_D65=true) {
33286       return RGBtoXYZ(use_D65).XYZtoxyY();
33287     }
33288 
33289     //! Convert pixel values from RGB to xyY color spaces \newinstance.
33290     CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const {
33291       return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65);
33292     }
33293 
33294     //! Convert pixel values from xyY to RGB color spaces.
33295     CImg<T>& xyYtoRGB(const bool use_D65=true) {
33296       return xyYtoXYZ().XYZtoRGB(use_D65);
33297     }
33298 
33299     //! Convert pixel values from xyY to RGB color spaces \newinstance.
33300     CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const {
33301       return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65);
33302     }
33303 
33304     //! Convert pixel values from RGB to CMYK color spaces.
33305     CImg<T>& RGBtoCMYK() {
33306       return RGBtoCMY().CMYtoCMYK();
33307     }
33308 
33309     //! Convert pixel values from RGB to CMYK color spaces \newinstance.
33310     CImg<Tfloat> get_RGBtoCMYK() const {
33311       return CImg<Tfloat>(*this,false).RGBtoCMYK();
33312     }
33313 
33314     //! Convert pixel values from CMYK to RGB color spaces.
33315     CImg<T>& CMYKtoRGB() {
33316       return CMYKtoCMY().CMYtoRGB();
33317     }
33318 
33319     //! Convert pixel values from CMYK to RGB color spaces \newinstance.
33320     CImg<Tuchar> get_CMYKtoRGB() const {
33321       return CImg<Tuchar>(*this,false).CMYKtoRGB();
33322     }
33323 
33324     //@}
33325     //------------------------------------------
33326     //
33327     //! \name Geometric / Spatial Manipulation
33328     //@{
33329     //------------------------------------------
33330 
33331     static float _cimg_lanczos(const float x) {
33332       if (x<=-2 || x>=2) return 0;
33333       const float a = (float)cimg::PI*x, b = 0.5f*a;
33334       return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
33335     }
33336 
33337     //! Resize image to new dimensions.
33338     /**
33339        \param size_x Number of columns (new size along the X-axis).
33340        \param size_y Number of rows (new size along the Y-axis).
33341        \param size_z Number of slices (new size along the Z-axis).
33342        \param size_c Number of vector-channels (new size along the C-axis).
33343        \param interpolation_type Method of interpolation:
33344        - -1 = no interpolation: raw memory resizing.
33345        - 0 = no interpolation: additional space is filled according to \p boundary_conditions.
33346        - 1 = nearest-neighbor interpolation.
33347        - 2 = moving average interpolation.
33348        - 3 = linear interpolation.
33349        - 4 = grid interpolation.
33350        - 5 = cubic interpolation.
33351        - 6 = lanczos interpolation.
33352        \param boundary_conditions Type of boundary conditions used if necessary.
33353        \param centering_x Set centering type (only if \p interpolation_type=0).
33354        \param centering_y Set centering type (only if \p interpolation_type=0).
33355        \param centering_z Set centering type (only if \p interpolation_type=0).
33356        \param centering_c Set centering type (only if \p interpolation_type=0).
33357        \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
33358     **/
33359     CImg<T>& resize(const int size_x, const int size_y=-100,
33360                     const int size_z=-100, const int size_c=-100,
33361                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
33362                     const float centering_x = 0, const float centering_y = 0,
33363                     const float centering_z = 0, const float centering_c = 0) {
33364       if (!size_x || !size_y || !size_z || !size_c) return assign();
33365       const unsigned int
33366         _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
33367         _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
33368         _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
33369         _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
33370         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
33371       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
33372       if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
33373       if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
33374         _width = sx; _height = sy; _depth = sz; _spectrum = sc;
33375         return *this;
33376       }
33377       return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
33378                         centering_x,centering_y,centering_z,centering_c).move_to(*this);
33379     }
33380 
33381     //! Resize image to new dimensions \newinstance.
33382     CImg<T> get_resize(const int size_x, const int size_y = -100,
33383                        const int size_z = -100, const int size_c = -100,
33384                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
33385                        const float centering_x = 0, const float centering_y = 0,
33386                        const float centering_z = 0, const float centering_c = 0) const {
33387       if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
33388           centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
33389         throw CImgArgumentException(_cimg_instance
33390                                     "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
33391                                     cimg_instance,
33392                                     centering_x,centering_y,centering_z,centering_c);
33393 
33394       if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
33395       const unsigned int
33396         sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)),
33397         sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)),
33398         sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)),
33399         sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100));
33400       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
33401       if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0);
33402       CImg<T> res;
33403       switch (interpolation_type) {
33404 
33405         // Raw resizing.
33406         //
33407       case -1 :
33408         std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc));
33409         break;
33410 
33411         // No interpolation.
33412         //
33413       case 0 : {
33414         const int
33415           xc = (int)(centering_x*((int)sx - width())),
33416           yc = (int)(centering_y*((int)sy - height())),
33417           zc = (int)(centering_z*((int)sz - depth())),
33418           cc = (int)(centering_c*((int)sc - spectrum()));
33419 
33420         switch (boundary_conditions) {
33421         case 3 : { // Mirror
33422           res.assign(sx,sy,sz,sc);
33423           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
33424           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),65536))
33425           cimg_forXYZC(res,x,y,z,c) {
33426             const int
33427               mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2),
33428               mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2);
33429             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
33430                                    my<height()?my:h2 - my - 1,
33431                                    mz<depth()?mz:d2 - mz - 1,
33432                                    mc<spectrum()?mc:s2 - mc - 1);
33433           }
33434         } break;
33435         case 2 : { // Periodic
33436           res.assign(sx,sy,sz,sc);
33437           const int
33438             x0 = ((int)xc%width()) - width(),
33439             y0 = ((int)yc%height()) - height(),
33440             z0 = ((int)zc%depth()) - depth(),
33441             c0 = ((int)cc%spectrum()) - spectrum(),
33442             dx = width(), dy = height(), dz = depth(), dc = spectrum();
33443           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),65536))
33444           for (int c = c0; c<(int)sc; c+=dc)
33445             for (int z = z0; z<(int)sz; z+=dz)
33446               for (int y = y0; y<(int)sy; y+=dy)
33447                 for (int x = x0; x<(int)sx; x+=dx)
33448                   res.draw_image(x,y,z,c,*this);
33449         } break;
33450         case 1 : { // Neumann
33451           res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
33452           CImg<T> sprite;
33453           if (xc>0) {  // X-backward
33454             res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33455             for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
33456           }
33457           if (xc + width()<(int)sx) { // X-forward
33458             res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1,
33459                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33460             for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
33461           }
33462           if (yc>0) {  // Y-backward
33463             res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33464             for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
33465           }
33466           if (yc + height()<(int)sy) { // Y-forward
33467             res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1,
33468                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33469             for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
33470           }
33471           if (zc>0) {  // Z-backward
33472             res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite);
33473             for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
33474           }
33475           if (zc + depth()<(int)sz) { // Z-forward
33476             res.get_crop(0,0,zc  +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
33477             for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
33478           }
33479           if (cc>0) {  // C-backward
33480             res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite);
33481             for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
33482           }
33483           if (cc + spectrum()<(int)sc) { // C-forward
33484             res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite);
33485             for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
33486           }
33487         } break;
33488         default : // Dirichlet
33489           res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this);
33490         }
33491         break;
33492       } break;
33493 
33494         // Nearest neighbor interpolation.
33495         //
33496       case 1 : {
33497         res.assign(sx,sy,sz,sc);
33498         CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1);
33499         const ulongT
33500           wh = (ulongT)_width*_height,
33501           whd = (ulongT)_width*_height*_depth,
33502           sxy = (ulongT)sx*sy,
33503           sxyz = (ulongT)sx*sy*sz,
33504           one = (ulongT)1;
33505         if (sx==_width) off_x.fill(1);
33506         else {
33507           ulongT *poff_x = off_x._data, curr = 0;
33508           cimg_forX(res,x) {
33509             const ulongT old = curr;
33510             curr = (x + one)*_width/sx;
33511             *(poff_x++) = curr - old;
33512           }
33513         }
33514         if (sy==_height) off_y.fill(_width);
33515         else {
33516           ulongT *poff_y = off_y._data, curr = 0;
33517           cimg_forY(res,y) {
33518             const ulongT old = curr;
33519             curr = (y + one)*_height/sy;
33520             *(poff_y++) = _width*(curr - old);
33521           }
33522           *poff_y = 0;
33523         }
33524         if (sz==_depth) off_z.fill(wh);
33525         else {
33526           ulongT *poff_z = off_z._data, curr = 0;
33527           cimg_forZ(res,z) {
33528             const ulongT old = curr;
33529             curr = (z + one)*_depth/sz;
33530             *(poff_z++) = wh*(curr - old);
33531           }
33532           *poff_z = 0;
33533         }
33534         if (sc==_spectrum) off_c.fill(whd);
33535         else {
33536           ulongT *poff_c = off_c._data, curr = 0;
33537           cimg_forC(res,c) {
33538             const ulongT old = curr;
33539             curr = (c + one)*_spectrum/sc;
33540             *(poff_c++) = whd*(curr - old);
33541           }
33542           *poff_c = 0;
33543         }
33544 
33545         T *ptrd = res._data;
33546         const T* ptrc = _data;
33547         const ulongT *poff_c = off_c._data;
33548         for (unsigned int c = 0; c<sc; ) {
33549           const T *ptrz = ptrc;
33550           const ulongT *poff_z = off_z._data;
33551           for (unsigned int z = 0; z<sz; ) {
33552             const T *ptry = ptrz;
33553             const ulongT *poff_y = off_y._data;
33554             for (unsigned int y = 0; y<sy; ) {
33555               const T *ptrx = ptry;
33556               const ulongT *poff_x = off_x._data;
33557               cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
33558               ++y;
33559               ulongT dy = *(poff_y++);
33560               for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
33561               ptry+=dy;
33562             }
33563             ++z;
33564             ulongT dz = *(poff_z++);
33565             for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd - sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
33566             ptrz+=dz;
33567           }
33568           ++c;
33569           ulongT dc = *(poff_c++);
33570           for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd - sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
33571           ptrc+=dc;
33572         }
33573       } break;
33574 
33575         // Moving average.
33576         //
33577       case 2 : {
33578         bool instance_first = true;
33579         if (sx!=_width) {
33580           CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
33581           for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
33582             const unsigned int d = std::min(b,c);
33583             a-=d; b-=d; c-=d;
33584             cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
33585             if (!b) {
33586               cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width;
33587               ++t;
33588               b = _width;
33589             }
33590             if (!c) { ++s; c = sx; }
33591           }
33592           tmp.move_to(res);
33593           instance_first = false;
33594         }
33595         if (sy!=_height) {
33596           CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
33597           for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
33598             const unsigned int d = std::min(b,c);
33599             a-=d; b-=d; c-=d;
33600             if (instance_first)
33601               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
33602             else
33603               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
33604             if (!b) {
33605               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height;
33606               ++t;
33607               b = _height;
33608             }
33609             if (!c) { ++s; c = sy; }
33610           }
33611           tmp.move_to(res);
33612           instance_first = false;
33613         }
33614         if (sz!=_depth) {
33615           CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
33616           for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
33617             const unsigned int d = std::min(b,c);
33618             a-=d; b-=d; c-=d;
33619             if (instance_first)
33620               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
33621             else
33622               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
33623             if (!b) {
33624               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth;
33625               ++t;
33626               b = _depth;
33627             }
33628             if (!c) { ++s; c = sz; }
33629           }
33630           tmp.move_to(res);
33631           instance_first = false;
33632         }
33633         if (sc!=_spectrum) {
33634           CImg<Tfloat> tmp(sx,sy,sz,sc,0);
33635           for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
33636             const unsigned int d = std::min(b,c);
33637             a-=d; b-=d; c-=d;
33638             if (instance_first)
33639               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
33640             else
33641               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
33642             if (!b) {
33643               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum;
33644               ++t;
33645               b = _spectrum;
33646             }
33647             if (!c) { ++s; c = sc; }
33648           }
33649           tmp.move_to(res);
33650           instance_first = false;
33651         }
33652       } break;
33653 
33654         // Linear interpolation.
33655         //
33656       case 3 : {
33657         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
33658         CImg<doubleT> foff(off._width);
33659         CImg<T> resx, resy, resz, resc;
33660         double curr, old;
33661 
33662         if (sx!=_width) {
33663           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
33664           else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
33665           else {
33666             const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
33667               (double)_width/sx;
33668             resx.assign(sx,_height,_depth,_spectrum);
33669             curr = old = 0;
33670             {
33671               unsigned int *poff = off._data;
33672               double *pfoff = foff._data;
33673               cimg_forX(resx,x) {
33674                 *(pfoff++) = curr - (unsigned int)curr;
33675                 old = curr;
33676                 curr = std::min(width() - 1.,curr + fx);
33677                 *(poff++) = (unsigned int)curr - (unsigned int)old;
33678               }
33679             }
33680             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resx.size(),65536))
33681               cimg_forYZC(resx,y,z,c) {
33682               const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1;
33683               T *ptrd = resx.data(0,y,z,c);
33684               const unsigned int *poff = off._data;
33685               const double *pfoff = foff._data;
33686               cimg_forX(resx,x) {
33687                 const double alpha = *(pfoff++);
33688                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1;
33689                 *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2);
33690                 ptrs+=*(poff++);
33691               }
33692             }
33693           }
33694         } else resx.assign(*this,true);
33695 
33696         if (sy!=_height) {
33697           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
33698           else {
33699             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
33700             else {
33701               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
33702                 (double)_height/sy;
33703               resy.assign(sx,sy,_depth,_spectrum);
33704               curr = old = 0;
33705               {
33706                 unsigned int *poff = off._data;
33707                 double *pfoff = foff._data;
33708                 cimg_forY(resy,y) {
33709                   *(pfoff++) = curr - (unsigned int)curr;
33710                   old = curr;
33711                   curr = std::min(height() - 1.,curr + fy);
33712                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
33713                 }
33714               }
33715               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resy.size(),65536))
33716               cimg_forXZC(resy,x,z,c) {
33717                 const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx;
33718                 T *ptrd = resy.data(x,0,z,c);
33719                 const unsigned int *poff = off._data;
33720                 const double *pfoff = foff._data;
33721                 cimg_forY(resy,y) {
33722                   const double alpha = *(pfoff++);
33723                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1;
33724                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
33725                   ptrd+=sx;
33726                   ptrs+=*(poff++);
33727                 }
33728               }
33729             }
33730           }
33731           resx.assign();
33732         } else resy.assign(resx,true);
33733 
33734         if (sz!=_depth) {
33735           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
33736           else {
33737             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
33738             else {
33739               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
33740                 (double)_depth/sz;
33741               const unsigned int sxy = sx*sy;
33742               resz.assign(sx,sy,sz,_spectrum);
33743               curr = old = 0;
33744               {
33745                 unsigned int *poff = off._data;
33746                 double *pfoff = foff._data;
33747                 cimg_forZ(resz,z) {
33748                   *(pfoff++) = curr - (unsigned int)curr;
33749                   old = curr;
33750                   curr = std::min(depth() - 1.,curr + fz);
33751                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
33752                 }
33753               }
33754               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resz.size(),65536))
33755               cimg_forXYC(resz,x,y,c) {
33756                 const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy;
33757                 T *ptrd = resz.data(x,y,0,c);
33758                 const unsigned int *poff = off._data;
33759                 const double *pfoff = foff._data;
33760                 cimg_forZ(resz,z) {
33761                   const double alpha = *(pfoff++);
33762                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1;
33763                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
33764                   ptrd+=sxy;
33765                   ptrs+=*(poff++);
33766                 }
33767               }
33768             }
33769           }
33770           resy.assign();
33771         } else resz.assign(resy,true);
33772 
33773         if (sc!=_spectrum) {
33774           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
33775           else {
33776             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
33777             else {
33778               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
33779                 (double)_spectrum/sc;
33780               const unsigned int sxyz = sx*sy*sz;
33781               resc.assign(sx,sy,sz,sc);
33782               curr = old = 0;
33783               {
33784                 unsigned int *poff = off._data;
33785                 double *pfoff = foff._data;
33786                 cimg_forC(resc,c) {
33787                   *(pfoff++) = curr - (unsigned int)curr;
33788                   old = curr;
33789                   curr = std::min(spectrum() - 1.,curr + fc);
33790                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
33791                 }
33792               }
33793               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resc.size(),65536))
33794               cimg_forXYZ(resc,x,y,z) {
33795                 const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz;
33796                 T *ptrd = resc.data(x,y,z,0);
33797                 const unsigned int *poff = off._data;
33798                 const double *pfoff = foff._data;
33799                 cimg_forC(resc,c) {
33800                   const double alpha = *(pfoff++);
33801                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1;
33802                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
33803                   ptrd+=sxyz;
33804                   ptrs+=*(poff++);
33805                 }
33806               }
33807             }
33808           }
33809           resz.assign();
33810         } else resc.assign(resz,true);
33811         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
33812       } break;
33813 
33814         // Grid interpolation.
33815         //
33816       case 4 : {
33817         CImg<T> resx, resy, resz, resc;
33818         if (sx!=_width) {
33819           if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
33820           else {
33821             resx.assign(sx,_height,_depth,_spectrum,(T)0);
33822             const int dx = (int)(2*sx), dy = 2*width();
33823             int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
33824             cimg_forX(resx,x) if ((err-=dy)<=0) {
33825               cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
33826               ++xs;
33827               err+=dx;
33828             }
33829           }
33830         } else resx.assign(*this,true);
33831 
33832         if (sy!=_height) {
33833           if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
33834           else {
33835             resy.assign(sx,sy,_depth,_spectrum,(T)0);
33836             const int dx = (int)(2*sy), dy = 2*height();
33837             int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
33838             cimg_forY(resy,y) if ((err-=dy)<=0) {
33839               cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
33840               ++ys;
33841               err+=dx;
33842             }
33843           }
33844           resx.assign();
33845         } else resy.assign(resx,true);
33846 
33847         if (sz!=_depth) {
33848           if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
33849           else {
33850             resz.assign(sx,sy,sz,_spectrum,(T)0);
33851             const int dx = (int)(2*sz), dy = 2*depth();
33852             int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
33853             cimg_forZ(resz,z) if ((err-=dy)<=0) {
33854               cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
33855               ++zs;
33856               err+=dx;
33857             }
33858           }
33859           resy.assign();
33860         } else resz.assign(resy,true);
33861 
33862         if (sc!=_spectrum) {
33863           if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
33864           else {
33865             resc.assign(sx,sy,sz,sc,(T)0);
33866             const int dx = (int)(2*sc), dy = 2*spectrum();
33867             int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
33868             cimg_forC(resc,c) if ((err-=dy)<=0) {
33869               cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
33870               ++cs;
33871               err+=dx;
33872             }
33873           }
33874           resz.assign();
33875         } else resc.assign(resz,true);
33876 
33877         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
33878       } break;
33879 
33880         // Cubic interpolation.
33881         //
33882       case 5 : {
33883         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
33884         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
33885         CImg<doubleT> foff(off._width);
33886         CImg<T> resx, resy, resz, resc;
33887         double curr, old;
33888 
33889         if (sx!=_width) {
33890           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
33891           else {
33892             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
33893             else {
33894               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
33895                 (double)_width/sx;
33896               resx.assign(sx,_height,_depth,_spectrum);
33897               curr = old = 0;
33898               {
33899                 unsigned int *poff = off._data;
33900                 double *pfoff = foff._data;
33901                 cimg_forX(resx,x) {
33902                   *(pfoff++) = curr - (unsigned int)curr;
33903                   old = curr;
33904                   curr = std::min(width() - 1.,curr + fx);
33905                   *(poff++) = (unsigned int)curr - (unsigned int)old;
33906                 }
33907               }
33908               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resx.size(),65536))
33909               cimg_forYZC(resx,y,z,c) {
33910                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2);
33911                 T *ptrd = resx.data(0,y,z,c);
33912                 const unsigned int *poff = off._data;
33913                 const double *pfoff = foff._data;
33914                 cimg_forX(resx,x) {
33915                   const double
33916                     t = *(pfoff++),
33917                     val1 = (double)*ptrs,
33918                     val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1,
33919                     val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1,
33920                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2,
33921                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
33922                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
33923                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
33924                   ptrs+=*(poff++);
33925                 }
33926               }
33927             }
33928           }
33929         } else resx.assign(*this,true);
33930 
33931         if (sy!=_height) {
33932           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
33933           else {
33934             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
33935             else {
33936               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
33937                 (double)_height/sy;
33938               resy.assign(sx,sy,_depth,_spectrum);
33939               curr = old = 0;
33940               {
33941                 unsigned int *poff = off._data;
33942                 double *pfoff = foff._data;
33943                 cimg_forY(resy,y) {
33944                   *(pfoff++) = curr - (unsigned int)curr;
33945                   old = curr;
33946                   curr = std::min(height() - 1.,curr + fy);
33947                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
33948                 }
33949               }
33950               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resy.size(),65536))
33951               cimg_forXZC(resy,x,z,c) {
33952                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx;
33953                 T *ptrd = resy.data(x,0,z,c);
33954                 const unsigned int *poff = off._data;
33955                 const double *pfoff = foff._data;
33956                 cimg_forY(resy,y) {
33957                   const double
33958                     t = *(pfoff++),
33959                     val1 = (double)*ptrs,
33960                     val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1,
33961                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1,
33962                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2,
33963                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
33964                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
33965                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
33966                   ptrd+=sx;
33967                   ptrs+=*(poff++);
33968                 }
33969               }
33970             }
33971           }
33972           resx.assign();
33973         } else resy.assign(resx,true);
33974 
33975         if (sz!=_depth) {
33976           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
33977           else {
33978             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
33979             else {
33980               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
33981                 (double)_depth/sz;
33982               const unsigned int sxy = sx*sy;
33983               resz.assign(sx,sy,sz,_spectrum);
33984               curr = old = 0;
33985               {
33986                 unsigned int *poff = off._data;
33987                 double *pfoff = foff._data;
33988                 cimg_forZ(resz,z) {
33989                   *(pfoff++) = curr - (unsigned int)curr;
33990                   old = curr;
33991                   curr = std::min(depth() - 1.,curr + fz);
33992                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
33993                 }
33994               }
33995               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resz.size(),65536))
33996               cimg_forXYC(resz,x,y,c) {
33997                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy;
33998                 T *ptrd = resz.data(x,y,0,c);
33999                 const unsigned int *poff = off._data;
34000                 const double *pfoff = foff._data;
34001                 cimg_forZ(resz,z) {
34002                   const double
34003                     t = *(pfoff++),
34004                     val1 = (double)*ptrs,
34005                     val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1,
34006                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1,
34007                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2,
34008                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34009                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34010                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34011                   ptrd+=sxy;
34012                   ptrs+=*(poff++);
34013                 }
34014               }
34015             }
34016           }
34017           resy.assign();
34018         } else resz.assign(resy,true);
34019 
34020         if (sc!=_spectrum) {
34021           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34022           else {
34023             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
34024             else {
34025               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
34026                 (double)_spectrum/sc;
34027               const unsigned int sxyz = sx*sy*sz;
34028               resc.assign(sx,sy,sz,sc);
34029               curr = old = 0;
34030               {
34031                 unsigned int *poff = off._data;
34032                 double *pfoff = foff._data;
34033                 cimg_forC(resc,c) {
34034                   *(pfoff++) = curr - (unsigned int)curr;
34035                   old = curr;
34036                   curr = std::min(spectrum() - 1.,curr + fc);
34037                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
34038                 }
34039               }
34040               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resc.size(),65536))
34041               cimg_forXYZ(resc,x,y,z) {
34042                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
34043                 T *ptrd = resc.data(x,y,z,0);
34044                 const unsigned int *poff = off._data;
34045                 const double *pfoff = foff._data;
34046                 cimg_forC(resc,c) {
34047                   const double
34048                     t = *(pfoff++),
34049                     val1 = (double)*ptrs,
34050                     val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1,
34051                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1,
34052                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2,
34053                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
34054                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
34055                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34056                   ptrd+=sxyz;
34057                   ptrs+=*(poff++);
34058                 }
34059               }
34060             }
34061           }
34062           resz.assign();
34063         } else resc.assign(resz,true);
34064 
34065         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34066       } break;
34067 
34068         // Lanczos interpolation.
34069         //
34070       case 6 : {
34071         const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max();
34072         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
34073         CImg<doubleT> foff(off._width);
34074         CImg<T> resx, resy, resz, resc;
34075         double curr, old;
34076 
34077         if (sx!=_width) {
34078           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
34079           else {
34080             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
34081             else {
34082               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
34083                 (double)_width/sx;
34084               resx.assign(sx,_height,_depth,_spectrum);
34085               curr = old = 0;
34086               {
34087                 unsigned int *poff = off._data;
34088                 double *pfoff = foff._data;
34089                 cimg_forX(resx,x) {
34090                   *(pfoff++) = curr - (unsigned int)curr;
34091                   old = curr;
34092                   curr = std::min(width() - 1.,curr + fx);
34093                   *(poff++) = (unsigned int)curr - (unsigned int)old;
34094                 }
34095               }
34096               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resx.size(),65536))
34097               cimg_forYZC(resx,y,z,c) {
34098                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
34099                   *const ptrsmax = ptrs0 + (_width - 2);
34100                 T *ptrd = resx.data(0,y,z,c);
34101                 const unsigned int *poff = off._data;
34102                 const double *pfoff = foff._data;
34103                 cimg_forX(resx,x) {
34104                   const double
34105                     t = *(pfoff++),
34106                     w0 = _cimg_lanczos(t + 2),
34107                     w1 = _cimg_lanczos(t + 1),
34108                     w2 = _cimg_lanczos(t),
34109                     w3 = _cimg_lanczos(t - 1),
34110                     w4 = _cimg_lanczos(t - 2),
34111                     val2 = (double)*ptrs,
34112                     val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2,
34113                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1,
34114                     val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2,
34115                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3,
34116                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34117                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
34118                   ptrs+=*(poff++);
34119                 }
34120               }
34121             }
34122           }
34123         } else resx.assign(*this,true);
34124 
34125         if (sy!=_height) {
34126           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
34127           else {
34128             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
34129             else {
34130               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
34131                 (double)_height/sy;
34132               resy.assign(sx,sy,_depth,_spectrum);
34133               curr = old = 0;
34134               {
34135                 unsigned int *poff = off._data;
34136                 double *pfoff = foff._data;
34137                 cimg_forY(resy,y) {
34138                   *(pfoff++) = curr - (unsigned int)curr;
34139                   old = curr;
34140                   curr = std::min(height() - 1.,curr + fy);
34141                   *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
34142                 }
34143               }
34144               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resy.size(),65536))
34145               cimg_forXZC(resy,x,z,c) {
34146                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
34147                   *const ptrsmax = ptrs0 + (_height - 2)*sx;
34148                 T *ptrd = resy.data(x,0,z,c);
34149                 const unsigned int *poff = off._data;
34150                 const double *pfoff = foff._data;
34151                 cimg_forY(resy,y) {
34152                   const double
34153                     t = *(pfoff++),
34154                     w0 = _cimg_lanczos(t + 2),
34155                     w1 = _cimg_lanczos(t + 1),
34156                     w2 = _cimg_lanczos(t),
34157                     w3 = _cimg_lanczos(t - 1),
34158                     w4 = _cimg_lanczos(t - 2),
34159                     val2 = (double)*ptrs,
34160                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2,
34161                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1,
34162                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2,
34163                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3,
34164                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34165                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34166                   ptrd+=sx;
34167                   ptrs+=*(poff++);
34168                 }
34169               }
34170             }
34171           }
34172           resx.assign();
34173         } else resy.assign(resx,true);
34174 
34175         if (sz!=_depth) {
34176           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
34177           else {
34178             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
34179             else {
34180               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
34181                 (double)_depth/sz;
34182               const unsigned int sxy = sx*sy;
34183               resz.assign(sx,sy,sz,_spectrum);
34184               curr = old = 0;
34185               {
34186                 unsigned int *poff = off._data;
34187                 double *pfoff = foff._data;
34188                 cimg_forZ(resz,z) {
34189                   *(pfoff++) = curr - (unsigned int)curr;
34190                   old = curr;
34191                   curr = std::min(depth() - 1.,curr + fz);
34192                   *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
34193                 }
34194               }
34195               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resz.size(),65536))
34196               cimg_forXYC(resz,x,y,c) {
34197                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
34198                   *const ptrsmax = ptrs0 + (_depth - 2)*sxy;
34199                 T *ptrd = resz.data(x,y,0,c);
34200                 const unsigned int *poff = off._data;
34201                 const double *pfoff = foff._data;
34202                 cimg_forZ(resz,z) {
34203                   const double
34204                     t = *(pfoff++),
34205                     w0 = _cimg_lanczos(t + 2),
34206                     w1 = _cimg_lanczos(t + 1),
34207                     w2 = _cimg_lanczos(t),
34208                     w3 = _cimg_lanczos(t - 1),
34209                     w4 = _cimg_lanczos(t - 2),
34210                     val2 = (double)*ptrs,
34211                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2,
34212                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1,
34213                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2,
34214                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3,
34215                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34216                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34217                   ptrd+=sxy;
34218                   ptrs+=*(poff++);
34219                 }
34220               }
34221             }
34222           }
34223           resy.assign();
34224         } else resz.assign(resy,true);
34225 
34226         if (sc!=_spectrum) {
34227           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
34228           else {
34229             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
34230             else {
34231               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
34232                 (double)_spectrum/sc;
34233               const unsigned int sxyz = sx*sy*sz;
34234               resc.assign(sx,sy,sz,sc);
34235               curr = old = 0;
34236               {
34237                 unsigned int *poff = off._data;
34238                 double *pfoff = foff._data;
34239                 cimg_forC(resc,c) {
34240                   *(pfoff++) = curr - (unsigned int)curr;
34241                   old = curr;
34242                   curr = std::min(spectrum() - 1.,curr + fc);
34243                   *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
34244                 }
34245               }
34246               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(resc.size(),65536))
34247               cimg_forXYZ(resc,x,y,z) {
34248                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
34249                   *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
34250                 T *ptrd = resc.data(x,y,z,0);
34251                 const unsigned int *poff = off._data;
34252                 const double *pfoff = foff._data;
34253                 cimg_forC(resc,c) {
34254                   const double
34255                     t = *(pfoff++),
34256                     w0 = _cimg_lanczos(t + 2),
34257                     w1 = _cimg_lanczos(t + 1),
34258                     w2 = _cimg_lanczos(t),
34259                     w3 = _cimg_lanczos(t - 1),
34260                     w4 = _cimg_lanczos(t - 2),
34261                     val2 = (double)*ptrs,
34262                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2,
34263                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1,
34264                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2,
34265                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3,
34266                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
34267                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
34268                   ptrd+=sxyz;
34269                   ptrs+=*(poff++);
34270                 }
34271               }
34272             }
34273           }
34274           resz.assign();
34275         } else resc.assign(resz,true);
34276 
34277         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
34278       } break;
34279 
34280         // Unknown interpolation.
34281         //
34282       default :
34283         throw CImgArgumentException(_cimg_instance
34284                                     "resize(): Invalid specified interpolation %d "
34285                                     "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
34286                                     "5=cubic | 6=lanczos }).",
34287                                     cimg_instance,
34288                                     interpolation_type);
34289       }
34290       return res;
34291     }
34292 
34293     //! Resize image to dimensions of another image.
34294     /**
34295        \param src Reference image used for dimensions.
34296        \param interpolation_type Interpolation method.
34297        \param boundary_conditions Boundary conditions.
34298        \param centering_x Set centering type (only if \p interpolation_type=0).
34299        \param centering_y Set centering type (only if \p interpolation_type=0).
34300        \param centering_z Set centering type (only if \p interpolation_type=0).
34301        \param centering_c Set centering type (only if \p interpolation_type=0).
34302      **/
34303     template<typename t>
34304     CImg<T>& resize(const CImg<t>& src,
34305                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
34306                     const float centering_x = 0, const float centering_y = 0,
34307                     const float centering_z = 0, const float centering_c = 0) {
34308       return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
34309                     centering_x,centering_y,centering_z,centering_c);
34310     }
34311 
34312     //! Resize image to dimensions of another image \newinstance.
34313     template<typename t>
34314     CImg<T> get_resize(const CImg<t>& src,
34315                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
34316                        const float centering_x = 0, const float centering_y = 0,
34317                        const float centering_z = 0, const float centering_c = 0) const {
34318       return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
34319                         centering_x,centering_y,centering_z,centering_c);
34320     }
34321 
34322     //! Resize image to dimensions of a display window.
34323     /**
34324        \param disp Reference display window used for dimensions.
34325        \param interpolation_type Interpolation method.
34326        \param boundary_conditions Boundary conditions.
34327        \param centering_x Set centering type (only if \p interpolation_type=0).
34328        \param centering_y Set centering type (only if \p interpolation_type=0).
34329        \param centering_z Set centering type (only if \p interpolation_type=0).
34330        \param centering_c Set centering type (only if \p interpolation_type=0).
34331      **/
34332     CImg<T>& resize(const CImgDisplay& disp,
34333                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
34334                     const float centering_x = 0, const float centering_y = 0,
34335                     const float centering_z = 0, const float centering_c = 0) {
34336       return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
34337                     centering_x,centering_y,centering_z,centering_c);
34338     }
34339 
34340     //! Resize image to dimensions of a display window \newinstance.
34341     CImg<T> get_resize(const CImgDisplay& disp,
34342                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
34343                        const float centering_x = 0, const float centering_y = 0,
34344                        const float centering_z = 0, const float centering_c = 0) const {
34345       return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
34346                         centering_x,centering_y,centering_z,centering_c);
34347     }
34348 
34349     //! Resize image to half-size along XY axes, using an optimized filter.
34350     CImg<T>& resize_halfXY() {
34351       return get_resize_halfXY().move_to(*this);
34352     }
34353 
34354     //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
34355     CImg<T> get_resize_halfXY() const {
34356       if (is_empty()) return *this;
34357       static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
34358                                         0.1231940459f,  0.1935127547f, 0.1231940459f,
34359                                         0.07842776544f, 0.1231940459f, 0.07842776544f };
34360       CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum);
34361       T *ptrd = res._data;
34362       cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
34363         if (x%2 && y%2) *(ptrd++) = (T)
34364                           (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] +
34365                            I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] +
34366                            I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]);
34367       return res;
34368     }
34369 
34370     //! Resize image to double-size, using the Scale2X algorithm.
34371     /**
34372        \note Use anisotropic upscaling algorithm
34373        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
34374     **/
34375     CImg<T>& resize_doubleXY() {
34376       return get_resize_doubleXY().move_to(*this);
34377     }
34378 
34379     //! Resize image to double-size, using the Scale2X algorithm \newinstance.
34380     CImg<T> get_resize_doubleXY() const {
34381 #define _cimg_gs2x_for3(bound,i) \
34382  for (int i = 0, _p1##i = 0, \
34383       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
34384       _n1##i<(int)(bound) || i==--_n1##i; \
34385       _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
34386 
34387 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
34388   _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
34389    _p1##x = 0, \
34390    _n1##x = (int)( \
34391    (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
34392    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
34393    (I[7] = (T)(img)(0,_n1##y,z,c)),     \
34394    1>=(img)._width?(img).width() - 1:1); \
34395    (_n1##x<(img).width() && ( \
34396    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
34397    (I[5] = (T)(img)(_n1##x,y,z,c)), \
34398    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
34399    x==--_n1##x; \
34400    I[1] = I[2], \
34401    I[3] = I[4], I[4] = I[5], \
34402    I[7] = I[8], \
34403    _p1##x = x++, ++_n1##x)
34404 
34405       if (is_empty()) return *this;
34406       CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
34407       CImg_3x3(I,T);
34408       cimg_forZC(*this,z,c) {
34409         T
34410           *ptrd1 = res.data(0,0,z,c),
34411           *ptrd2 = ptrd1 + res._width;
34412         _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
34413           if (Icp!=Icn && Ipc!=Inc) {
34414             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
34415             *(ptrd1++) = Icp==Inc?Inc:Icc;
34416             *(ptrd2++) = Ipc==Icn?Ipc:Icc;
34417             *(ptrd2++) = Icn==Inc?Inc:Icc;
34418           } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
34419         }
34420       }
34421       return res;
34422     }
34423 
34424     //! Resize image to triple-size, using the Scale3X algorithm.
34425     /**
34426        \note Use anisotropic upscaling algorithm
34427        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
34428     **/
34429     CImg<T>& resize_tripleXY() {
34430       return get_resize_tripleXY().move_to(*this);
34431     }
34432 
34433     //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
34434     CImg<T> get_resize_tripleXY() const {
34435 #define _cimg_gs3x_for3(bound,i) \
34436  for (int i = 0, _p1##i = 0, \
34437       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
34438       _n1##i<(int)(bound) || i==--_n1##i; \
34439       _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
34440 
34441 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
34442   _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
34443    _p1##x = 0, \
34444    _n1##x = (int)( \
34445    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
34446    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
34447    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
34448    1>=(img)._width?(img).width() - 1:1); \
34449    (_n1##x<(img).width() && ( \
34450    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
34451    (I[5] = (T)(img)(_n1##x,y,z,c)), \
34452    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
34453    x==--_n1##x; \
34454    I[0] = I[1], I[1] = I[2], \
34455    I[3] = I[4], I[4] = I[5], \
34456    I[6] = I[7], I[7] = I[8], \
34457    _p1##x = x++, ++_n1##x)
34458 
34459       if (is_empty()) return *this;
34460       CImg<T> res(3*_width,3*_height,_depth,_spectrum);
34461       CImg_3x3(I,T);
34462       cimg_forZC(*this,z,c) {
34463         T
34464           *ptrd1 = res.data(0,0,z,c),
34465           *ptrd2 = ptrd1 + res._width,
34466           *ptrd3 = ptrd2 + res._width;
34467         _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
34468           if (Icp != Icn && Ipc != Inc) {
34469             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
34470             *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
34471             *(ptrd1++) = Icp==Inc?Inc:Icc;
34472             *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
34473             *(ptrd2++) = Icc;
34474             *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
34475             *(ptrd3++) = Ipc==Icn?Ipc:Icc;
34476             *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
34477             *(ptrd3++) = Icn==Inc?Inc:Icc;
34478           } else {
34479             *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
34480             *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
34481             *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
34482           }
34483         }
34484       }
34485       return res;
34486     }
34487 
34488     //! Mirror image content along specified axis.
34489     /**
34490        \param axis Mirror axis
34491     **/
34492     CImg<T>& mirror(const char axis) {
34493       if (is_empty()) return *this;
34494       T *pf, *pb, *buf = 0;
34495       switch (cimg::lowercase(axis)) {
34496       case 'x' : {
34497         pf = _data; pb = data(_width - 1);
34498         const unsigned int width2 = _width/2;
34499         for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
34500           for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
34501           pf+=_width - width2;
34502           pb+=_width + width2;
34503         }
34504       } break;
34505       case 'y' : {
34506         buf = new T[_width];
34507         pf = _data; pb = data(0,_height - 1);
34508         const unsigned int height2 = _height/2;
34509         for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
34510           for (unsigned int y = 0; y<height2; ++y) {
34511             std::memcpy(buf,pf,_width*sizeof(T));
34512             std::memcpy(pf,pb,_width*sizeof(T));
34513             std::memcpy(pb,buf,_width*sizeof(T));
34514             pf+=_width;
34515             pb-=_width;
34516           }
34517           pf+=(ulongT)_width*(_height - height2);
34518           pb+=(ulongT)_width*(_height + height2);
34519         }
34520       } break;
34521       case 'z' : {
34522         buf = new T[(ulongT)_width*_height];
34523         pf = _data; pb = data(0,0,_depth - 1);
34524         const unsigned int depth2 = _depth/2;
34525         cimg_forC(*this,c) {
34526           for (unsigned int z = 0; z<depth2; ++z) {
34527             std::memcpy(buf,pf,_width*_height*sizeof(T));
34528             std::memcpy(pf,pb,_width*_height*sizeof(T));
34529             std::memcpy(pb,buf,_width*_height*sizeof(T));
34530             pf+=(ulongT)_width*_height;
34531             pb-=(ulongT)_width*_height;
34532           }
34533           pf+=(ulongT)_width*_height*(_depth - depth2);
34534           pb+=(ulongT)_width*_height*(_depth + depth2);
34535         }
34536       } break;
34537       case 'c' : {
34538         buf = new T[(ulongT)_width*_height*_depth];
34539         pf = _data; pb = data(0,0,0,_spectrum - 1);
34540         const unsigned int _spectrum2 = _spectrum/2;
34541         for (unsigned int v = 0; v<_spectrum2; ++v) {
34542           std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
34543           std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
34544           std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
34545           pf+=(ulongT)_width*_height*_depth;
34546           pb-=(ulongT)_width*_height*_depth;
34547         }
34548       } break;
34549       default :
34550         throw CImgArgumentException(_cimg_instance
34551                                     "mirror(): Invalid specified axis '%c'.",
34552                                     cimg_instance,
34553                                     axis);
34554       }
34555       delete[] buf;
34556       return *this;
34557     }
34558 
34559     //! Mirror image content along specified axis \newinstance.
34560     CImg<T> get_mirror(const char axis) const {
34561       return (+*this).mirror(axis);
34562     }
34563 
34564     //! Mirror image content along specified axes.
34565     /**
34566        \param axes Mirror axes, as a C-string.
34567        \note \c axes may contains multiple characters, e.g. \c "xyz"
34568     **/
34569     CImg<T>& mirror(const char *const axes) {
34570       for (const char *s = axes; *s; ++s) mirror(*s);
34571       return *this;
34572     }
34573 
34574     //! Mirror image content along specified axes \newinstance.
34575     CImg<T> get_mirror(const char *const axes) const {
34576       return (+*this).mirror(axes);
34577     }
34578 
34579     //! Shift image content.
34580     /**
34581        \param delta_x Amount of displacement along the X-axis.
34582        \param delta_y Amount of displacement along the Y-axis.
34583        \param delta_z Amount of displacement along the Z-axis.
34584        \param delta_c Amount of displacement along the C-axis.
34585        \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
34586     **/
34587     CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
34588                    const unsigned int boundary_conditions=0) {
34589       if (is_empty()) return *this;
34590       if (boundary_conditions==3)
34591         return get_crop(-delta_x,-delta_y,-delta_z,-delta_c,
34592                         width() - delta_x - 1,
34593                         height() - delta_y - 1,
34594                         depth() - delta_z - 1,
34595                         spectrum() - delta_c - 1,3).move_to(*this);
34596       if (delta_x) // Shift along X-axis
34597         switch (boundary_conditions) {
34598         case 2 : { // Periodic
34599           const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
34600           if (!ndelta_x) return *this;
34601           CImg<T> buf(cimg::abs(ndelta_x));
34602           if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
34603               std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
34604               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
34605               std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
34606             } else cimg_forYZC(*this,y,z,c) {
34607               std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T));
34608               std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T));
34609               std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
34610             }
34611         } break;
34612         case 1 : // Neumann
34613           if (delta_x<0) {
34614             const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x;
34615             if (!ndelta_x) return *this;
34616             cimg_forYZC(*this,y,z,c) {
34617               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
34618               T *ptrd = data(_width - 1,y,z,c);
34619               const T val = *ptrd;
34620               for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val;
34621             }
34622           } else {
34623             const int ndelta_x = (delta_x>=width())?width() - 1:delta_x;
34624             if (!ndelta_x) return *this;
34625             cimg_forYZC(*this,y,z,c) {
34626               std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
34627               T *ptrd = data(0,y,z,c);
34628               const T val = *ptrd;
34629               for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val;
34630             }
34631           }
34632           break;
34633         default : // Dirichlet
34634           if (delta_x<=-width() || delta_x>=width()) return fill((T)0);
34635           if (delta_x<0) cimg_forYZC(*this,y,z,c) {
34636               std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T));
34637               std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T));
34638             } else cimg_forYZC(*this,y,z,c) {
34639               std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
34640               std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
34641             }
34642         }
34643 
34644       if (delta_y) // Shift along Y-axis
34645         switch (boundary_conditions) {
34646         case 2 : { // Periodic
34647           const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
34648           if (!ndelta_y) return *this;
34649           CImg<T> buf(width(),cimg::abs(ndelta_y));
34650           if (ndelta_y>0) cimg_forZC(*this,z,c) {
34651               std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
34652               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
34653               std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
34654             } else cimg_forZC(*this,z,c) {
34655               std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
34656               std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T));
34657               std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
34658             }
34659         } break;
34660         case 1 : // Neumann
34661           if (delta_y<0) {
34662             const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y;
34663             if (!ndelta_y) return *this;
34664             cimg_forZC(*this,z,c) {
34665               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
34666               T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c);
34667               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
34668             }
34669           } else {
34670             const int ndelta_y = (delta_y>=height())?height() - 1:delta_y;
34671             if (!ndelta_y) return *this;
34672             cimg_forZC(*this,z,c) {
34673               std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
34674               T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
34675               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
34676             }
34677           }
34678           break;
34679         default : // Dirichlet
34680           if (delta_y<=-height() || delta_y>=height()) return fill((T)0);
34681           if (delta_y<0) cimg_forZC(*this,z,c) {
34682               std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T));
34683               std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T));
34684             } else cimg_forZC(*this,z,c) {
34685               std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
34686               std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
34687             }
34688         }
34689 
34690       if (delta_z) // Shift along Z-axis
34691         switch (boundary_conditions) {
34692         case 2 : { // Periodic
34693           const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
34694           if (!ndelta_z) return *this;
34695           CImg<T> buf(width(),height(),cimg::abs(ndelta_z));
34696           if (ndelta_z>0) cimg_forC(*this,c) {
34697               std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
34698               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
34699               std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
34700             } else cimg_forC(*this,c) {
34701               std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
34702               std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T));
34703               std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
34704             }
34705         } break;
34706         case 1 : // Neumann
34707           if (delta_z<0) {
34708             const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z;
34709             if (!ndelta_z) return *this;
34710             cimg_forC(*this,c) {
34711               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
34712               T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c);
34713               for (int l = 0; l<ndelta_z - 1; ++l) {
34714                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
34715               }
34716             }
34717           } else {
34718             const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z;
34719             if (!ndelta_z) return *this;
34720             cimg_forC(*this,c) {
34721               std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
34722               T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
34723               for (int l = 0; l<ndelta_z - 1; ++l) {
34724                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
34725               }
34726             }
34727           }
34728           break;
34729         default : // Dirichlet
34730           if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0);
34731           if (delta_z<0) cimg_forC(*this,c) {
34732               std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T));
34733               std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
34734             } else cimg_forC(*this,c) {
34735               std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
34736               std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
34737             }
34738         }
34739 
34740       if (delta_c) // Shift along C-axis
34741         switch (boundary_conditions) {
34742         case 2 : { // Periodic
34743           const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
34744           if (!ndelta_c) return *this;
34745           CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c));
34746           if (ndelta_c>0) {
34747             std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
34748             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
34749             std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
34750           } else {
34751             std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
34752             std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T));
34753             std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
34754           }
34755         } break;
34756         case 1 : // Neumann
34757           if (delta_c<0) {
34758             const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c;
34759             if (!ndelta_c) return *this;
34760             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
34761             T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1);
34762             for (int l = 0; l<ndelta_c - 1; ++l) {
34763               std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
34764             }
34765           } else {
34766             const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c;
34767             if (!ndelta_c) return *this;
34768             std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
34769             T *ptrd = data(0,0,0,1);
34770             for (int l = 0; l<ndelta_c - 1; ++l) {
34771               std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
34772             }
34773           }
34774           break;
34775         default : // Dirichlet
34776           if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0);
34777           if (delta_c<0) {
34778             std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T));
34779             std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
34780           } else {
34781             std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
34782             std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
34783           }
34784         }
34785       return *this;
34786     }
34787 
34788     //! Shift image content \newinstance.
34789     CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
34790                       const unsigned int boundary_conditions=0) const {
34791       return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
34792     }
34793 
34794     //! Permute axes order.
34795     /**
34796        \param axes_order Axes permutations, as a C-string of 4 characters.
34797        This function permutes image content regarding the specified axes permutation.
34798     **/
34799     CImg<T>& permute_axes(const char *const axes_order) {
34800       return get_permute_axes(axes_order).move_to(*this);
34801     }
34802 
34803     //! Permute axes order \newinstance.
34804     CImg<T> get_permute_axes(const char *const axes_order) const {
34805       const T foo = (T)0;
34806       return _permute_axes(axes_order,foo);
34807     }
34808 
34809     template<typename t>
34810     CImg<t> _permute_axes(const char *const axes_order, const t&) const {
34811       if (is_empty() || !axes_order) return CImg<t>(*this,false);
34812       CImg<t> res;
34813       const T* ptrs = _data;
34814       unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 };
34815       for (unsigned int l = 0; axes_order[l]; ++l) {
34816         int c = cimg::lowercase(axes_order[l]);
34817         if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; }
34818         else { ++n_code[c%=4]; s_code[l] = c; }
34819       }
34820       if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
34821         const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
34822         ulongT wh, whd;
34823         switch (code) {
34824         case 0x0123 : // xyzc
34825           return +*this;
34826         case 0x0132 : // xycz
34827           res.assign(_width,_height,_spectrum,_depth);
34828           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34829           cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
34830           break;
34831         case 0x0213 : // xzyc
34832           res.assign(_width,_depth,_height,_spectrum);
34833           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34834           cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
34835           break;
34836         case 0x0231 : // xzcy
34837           res.assign(_width,_depth,_spectrum,_height);
34838           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34839           cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
34840           break;
34841         case 0x0312 : // xcyz
34842           res.assign(_width,_spectrum,_height,_depth);
34843           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34844           cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
34845           break;
34846         case 0x0321 : // xczy
34847           res.assign(_width,_spectrum,_depth,_height);
34848           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34849           cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
34850           break;
34851         case 0x1023 : // yxzc
34852           res.assign(_height,_width,_depth,_spectrum);
34853           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34854           cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
34855           break;
34856         case 0x1032 : // yxcz
34857           res.assign(_height,_width,_spectrum,_depth);
34858           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34859           cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
34860           break;
34861         case 0x1203 : // yzxc
34862           res.assign(_height,_depth,_width,_spectrum);
34863           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34864           cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
34865           break;
34866         case 0x1230 : // yzcx
34867           res.assign(_height,_depth,_spectrum,_width);
34868           switch (_width) {
34869           case 1 : {
34870             t *ptr_r = res.data(0,0,0,0);
34871             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
34872               *(ptr_r++) = (t)*(ptrs++);
34873             }
34874           } break;
34875           case 2 : {
34876             t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
34877             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
34878               *(ptr_r++) = (t)ptrs[0];
34879               *(ptr_g++) = (t)ptrs[1];
34880               ptrs+=2;
34881             }
34882           } break;
34883           case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
34884             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);
34885             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
34886               *(ptr_r++) = (t)ptrs[0];
34887               *(ptr_g++) = (t)ptrs[1];
34888               *(ptr_b++) = (t)ptrs[2];
34889               ptrs+=3;
34890             }
34891           } break;
34892           case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
34893             t
34894               *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1),
34895               *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
34896             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
34897               *(ptr_r++) = (t)ptrs[0];
34898               *(ptr_g++) = (t)ptrs[1];
34899               *(ptr_b++) = (t)ptrs[2];
34900               *(ptr_a++) = (t)ptrs[3];
34901               ptrs+=4;
34902             }
34903           } break;
34904           default : {
34905             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34906             cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
34907             return res;
34908           }
34909           }
34910           break;
34911         case 0x1302 : // ycxz
34912           res.assign(_height,_spectrum,_width,_depth);
34913           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34914           cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
34915           break;
34916         case 0x1320 : // yczx
34917           res.assign(_height,_spectrum,_depth,_width);
34918           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34919           cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
34920           break;
34921         case 0x2013 : // zxyc
34922           res.assign(_depth,_width,_height,_spectrum);
34923           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34924           cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
34925           break;
34926         case 0x2031 : // zxcy
34927           res.assign(_depth,_width,_spectrum,_height);
34928           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34929           cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
34930           break;
34931         case 0x2103 : // zyxc
34932           res.assign(_depth,_height,_width,_spectrum);
34933           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34934           cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
34935           break;
34936         case 0x2130 : // zycx
34937           res.assign(_depth,_height,_spectrum,_width);
34938           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34939           cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
34940           break;
34941         case 0x2301 : // zcxy
34942           res.assign(_depth,_spectrum,_width,_height);
34943           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34944           cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
34945           break;
34946         case 0x2310 : // zcyx
34947           res.assign(_depth,_spectrum,_height,_width);
34948           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34949           cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
34950           break;
34951         case 0x3012 : // cxyz
34952           res.assign(_spectrum,_width,_height,_depth);
34953           switch (_spectrum) {
34954           case 1 : {
34955             const T *ptr_r = data(0,0,0,0);
34956             t *ptrd = res._data;
34957             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
34958           } break;
34959           case 2 : {
34960             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
34961             t *ptrd = res._data;
34962             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
34963               ptrd[0] = (t)*(ptr_r++);
34964               ptrd[1] = (t)*(ptr_g++);
34965               ptrd+=2;
34966             }
34967           } break;
34968           case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
34969             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
34970             t *ptrd = res._data;
34971             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
34972               ptrd[0] = (t)*(ptr_r++);
34973               ptrd[1] = (t)*(ptr_g++);
34974               ptrd[2] = (t)*(ptr_b++);
34975               ptrd+=3;
34976             }
34977           } break;
34978           case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
34979             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);
34980             t *ptrd = res._data;
34981             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
34982               ptrd[0] = (t)*(ptr_r++);
34983               ptrd[1] = (t)*(ptr_g++);
34984               ptrd[2] = (t)*(ptr_b++);
34985               ptrd[3] = (t)*(ptr_a++);
34986               ptrd+=4;
34987             }
34988           } break;
34989           default : {
34990             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34991             cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
34992           }
34993           }
34994           break;
34995         case 0x3021 : // cxzy
34996           res.assign(_spectrum,_width,_depth,_height);
34997           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
34998           cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
34999           break;
35000         case 0x3102 : // cyxz
35001           res.assign(_spectrum,_height,_width,_depth);
35002           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35003           cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
35004           break;
35005         case 0x3120 : // cyzx
35006           res.assign(_spectrum,_height,_depth,_width);
35007           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35008           cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
35009           break;
35010         case 0x3201 : // czxy
35011           res.assign(_spectrum,_depth,_width,_height);
35012           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35013           cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
35014           break;
35015         case 0x3210 : // czyx
35016           res.assign(_spectrum,_depth,_height,_width);
35017           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
35018           cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
35019           break;
35020         }
35021       }
35022       if (!res)
35023         throw CImgArgumentException(_cimg_instance
35024                                     "permute_axes(): Invalid specified axes order '%s'.",
35025                                     cimg_instance,
35026                                     axes_order);
35027       return res;
35028     }
35029 
35030     //! Unroll pixel values along specified axis.
35031     /**
35032        \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
35033     **/
35034     CImg<T>& unroll(const char axis) {
35035       const unsigned int siz = (unsigned int)size();
35036       if (siz) switch (cimg::lowercase(axis)) {
35037       case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
35038       case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
35039       case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
35040       case 'c' : _spectrum = siz; _width = _height = _depth = 1; break;
35041       }
35042       return *this;
35043     }
35044 
35045     //! Unroll pixel values along specified axis \newinstance.
35046     CImg<T> get_unroll(const char axis) const {
35047       return (+*this).unroll(axis);
35048     }
35049 
35050     //! Rotate image with arbitrary angle.
35051     /**
35052        \param angle Rotation angle, in degrees.
35053        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
35054        \param boundary_conditions Boundary conditions.
35055               Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35056        \note The size of the image is modified.
35057     **/
35058     CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
35059                     const unsigned int boundary_conditions=0) {
35060       const float nangle = cimg::mod(angle,360.f);
35061       if (nangle==0.f) return *this;
35062       return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);
35063     }
35064 
35065     //! Rotate image with arbitrary angle \newinstance.
35066     CImg<T> get_rotate(const float angle, const unsigned int interpolation=1,
35067                        const unsigned int boundary_conditions=0) const {
35068       if (is_empty()) return *this;
35069       CImg<T> res;
35070       const float nangle = cimg::mod(angle,360.f);
35071       if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles
35072         const int wm1 = width() - 1, hm1 = height() - 1;
35073         const int iangle = (int)nangle/90;
35074         switch (iangle) {
35075         case 1 : { // 90 deg
35076           res.assign(_height,_width,_depth,_spectrum);
35077           T *ptrd = res._data;
35078           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c);
35079         } break;
35080         case 2 : { // 180 deg
35081           res.assign(_width,_height,_depth,_spectrum);
35082           T *ptrd = res._data;
35083           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c);
35084         } break;
35085         case 3 : { // 270 deg
35086           res.assign(_height,_width,_depth,_spectrum);
35087           T *ptrd = res._data;
35088           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c);
35089         } break;
35090         default : // 0 deg
35091           return *this;
35092         }
35093       } else { // Generic angle
35094         const float
35095           rad = (float)(nangle*cimg::PI/180.),
35096           ca = (float)std::cos(rad), sa = (float)std::sin(rad),
35097           ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa),
35098           vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca),
35099           w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1);
35100         res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum);
35101         const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1);
35102         _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2);
35103       }
35104       return res;
35105     }
35106 
35107     //! Rotate image with arbitrary angle, around a center point.
35108     /**
35109        \param angle Rotation angle, in degrees.
35110        \param cx X-coordinate of the rotation center.
35111        \param cy Y-coordinate of the rotation center.
35112        \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
35113        \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35114     **/
35115     CImg<T>& rotate(const float angle, const float cx, const float cy,
35116                     const unsigned int interpolation, const unsigned int boundary_conditions=0) {
35117       return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this);
35118     }
35119 
35120     //! Rotate image with arbitrary angle, around a center point \newinstance.
35121     CImg<T> get_rotate(const float angle, const float cx, const float cy,
35122                        const unsigned int interpolation, const unsigned int boundary_conditions=0) const {
35123       if (is_empty()) return *this;
35124       CImg<T> res(_width,_height,_depth,_spectrum);
35125       _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy);
35126       return res;
35127     }
35128 
35129     // [internal] Perform 2D rotation with arbitrary angle.
35130     void _rotate(CImg<T>& res, const float angle,
35131                  const unsigned int interpolation, const unsigned int boundary_conditions,
35132                  const float w2, const float h2,
35133                  const float rw2, const float rh2) const {
35134       const float
35135         rad = (float)(angle*cimg::PI/180.),
35136         ca = (float)std::cos(rad), sa = (float)std::sin(rad);
35137 
35138       switch (boundary_conditions) {
35139       case 3 : { // Mirror
35140 
35141         switch (interpolation) {
35142         case 2 : { // Cubic interpolation
35143           const float ww = 2.f*width(), hh = 2.f*height();
35144           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35145             cimg_forXYZC(res,x,y,z,c) {
35146             const float xc = x - rw2, yc = y - rh2,
35147               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
35148               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
35149             res(x,y,z,c) = _cubic_atXY_c(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35150           }
35151         } break;
35152         case 1 : { // Linear interpolation
35153           const float ww = 2.f*width(), hh = 2.f*height();
35154           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35155             cimg_forXYZC(res,x,y,z,c) {
35156             const float xc = x - rw2, yc = y - rh2,
35157               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
35158               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
35159             res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35160           }
35161         } break;
35162         default : { // Nearest-neighbor interpolation
35163           const int ww = 2*width(), hh = 2*height();
35164           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35165             cimg_forXYZC(res,x,y,z,c) {
35166             const float xc = x - rw2, yc = y - rh2,
35167               mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww),
35168               my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh);
35169             res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
35170           }
35171         }
35172         }
35173       } break;
35174 
35175       case 2 : // Periodic
35176         switch (interpolation) {
35177         case 2 : { // Cubic interpolation
35178           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35179             cimg_forXYZC(res,x,y,z,c) {
35180             const float xc = x - rw2, yc = y - rh2;
35181             res(x,y,z,c) = _cubic_atXY_pc(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35182           }
35183         } break;
35184         case 1 : { // Linear interpolation
35185           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35186             cimg_forXYZC(res,x,y,z,c) {
35187             const float xc = x - rw2, yc = y - rh2;
35188             res(x,y,z,c) = (T)_linear_atXY_p(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35189           }
35190         } break;
35191         default : { // Nearest-neighbor interpolation
35192           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35193             cimg_forXYZC(res,x,y,z,c) {
35194             const float xc = x - rw2, yc = y - rh2;
35195             res(x,y,z,c) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()),
35196                                    cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c);
35197           }
35198         }
35199         } break;
35200 
35201       case 1 : // Neumann
35202         switch (interpolation) {
35203         case 2 : { // Cubic interpolation
35204           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35205           cimg_forXYZC(res,x,y,z,c) {
35206             const float xc = x - rw2, yc = y - rh2;
35207             res(x,y,z,c) = _cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35208           }
35209         } break;
35210         case 1 : { // Linear interpolation
35211           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35212           cimg_forXYZC(res,x,y,z,c) {
35213             const float xc = x - rw2, yc = y - rh2;
35214             res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
35215           }
35216         } break;
35217         default : { // Nearest-neighbor interpolation
35218           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35219           cimg_forXYZC(res,x,y,z,c) {
35220             const float xc = x - rw2, yc = y - rh2;
35221             res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa),
35222                                  (int)cimg::round(h2 - xc*sa + yc*ca),z,c);
35223           }
35224         }
35225         } break;
35226 
35227       default : // Dirichlet
35228         switch (interpolation) {
35229         case 2 : { // Cubic interpolation
35230           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35231           cimg_forXYZC(res,x,y,z,c) {
35232             const float xc = x - rw2, yc = y - rh2;
35233             res(x,y,z,c) = cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
35234           }
35235         } break;
35236         case 1 : { // Linear interpolation
35237           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35238           cimg_forXYZC(res,x,y,z,c) {
35239             const float xc = x - rw2, yc = y - rh2;
35240             res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
35241           }
35242         } break;
35243         default : { // Nearest-neighbor interpolation
35244           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
35245           cimg_forXYZC(res,x,y,z,c) {
35246             const float xc = x - rw2, yc = y - rh2;
35247             res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa),
35248                                 (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0);
35249           }
35250         }
35251         }
35252       }
35253     }
35254 
35255     //! Rotate volumetric image with arbitrary angle and axis.
35256     /**
35257        \param u X-coordinate of the 3D rotation axis.
35258        \param v Y-coordinate of the 3D rotation axis.
35259        \param w Z-coordinate of the 3D rotation axis.
35260        \param angle Rotation angle, in degrees.
35261        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
35262        \param boundary_conditions Boundary conditions.
35263               Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35264        \note Most of the time, size of the image is modified.
35265     **/
35266     CImg<T> rotate(const float u, const float v, const float w, const float angle,
35267                    const unsigned int interpolation, const unsigned int boundary_conditions) {
35268       const float nangle = cimg::mod(angle,360.f);
35269       if (nangle==0.f) return *this;
35270       return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);
35271     }
35272 
35273     //! Rotate volumetric image with arbitrary angle and axis \newinstance.
35274     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
35275                        const unsigned int interpolation, const unsigned int boundary_conditions) const {
35276       if (is_empty()) return *this;
35277       CImg<T> res;
35278       const float
35279         w1 = _width - 1, h1 = _height - 1, d1 = _depth -1,
35280         w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1;
35281       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle);
35282       const CImg<Tfloat>
35283         X = R*CImg<Tfloat>(8,3,1,1,
35284                            0.f,w1,w1,0.f,0.f,w1,w1,0.f,
35285                            0.f,0.f,h1,h1,0.f,0.f,h1,h1,
35286                            0.f,0.f,0.f,0.f,d1,d1,d1,d1);
35287       float
35288         xm, xM = X.get_shared_row(0).max_min(xm),
35289         ym, yM = X.get_shared_row(1).max_min(ym),
35290         zm, zM = X.get_shared_row(2).max_min(zm);
35291       const int
35292         dx = (int)cimg::round(xM - xm),
35293         dy = (int)cimg::round(yM - ym),
35294         dz = (int)cimg::round(zM - zm);
35295       R.transpose();
35296       res.assign(1 + dx,1 + dy,1 + dz,_spectrum);
35297       const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz;
35298       _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2);
35299       return res;
35300     }
35301 
35302     //! Rotate volumetric image with arbitrary angle and axis, around a center point.
35303     /**
35304        \param u X-coordinate of the 3D rotation axis.
35305        \param v Y-coordinate of the 3D rotation axis.
35306        \param w Z-coordinate of the 3D rotation axis.
35307        \param angle Rotation angle, in degrees.
35308        \param cx X-coordinate of the rotation center.
35309        \param cy Y-coordinate of the rotation center.
35310        \param cz Z-coordinate of the rotation center.
35311        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
35312        \param boundary_conditions Boundary conditions. Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic }</tt>.
35313        \note Most of the time, size of the image is modified.
35314     **/
35315     CImg<T> rotate(const float u, const float v, const float w, const float angle,
35316                    const float cx, const float cy, const float cz,
35317                    const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
35318       const float nangle = cimg::mod(angle,360.f);
35319       if (nangle==0.f) return *this;
35320       return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
35321     }
35322 
35323     //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance.
35324     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
35325                        const float cx, const float cy, const float cz,
35326                        const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
35327       if (is_empty()) return *this;
35328       CImg<T> res(_width,_height,_depth,_spectrum);
35329       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle);
35330       _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz);
35331       return res;
35332     }
35333 
35334     // [internal] Perform 3D rotation with arbitrary axis and angle.
35335     void _rotate(CImg<T>& res, const CImg<Tfloat>& R,
35336                  const unsigned int interpolation, const unsigned int boundary_conditions,
35337                  const float w2, const float h2, const float d2,
35338                  const float rw2, const float rh2, const float rd2) const {
35339       switch (boundary_conditions) {
35340       case 3 : // Mirror
35341         switch (interpolation) {
35342         case 2 : { // Cubic interpolation
35343           const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
35344           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35345           cimg_forXYZ(res,x,y,z) {
35346             const float
35347               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35348               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
35349               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
35350               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
35351             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X<width()?X:ww - X - 1,
35352                                                            Y<height()?Y:hh - Y - 1,
35353                                                            Z<depth()?Z:dd - Z - z,c);
35354           }
35355         } break;
35356         case 1 : { // Linear interpolation
35357           const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
35358           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35359           cimg_forXYZ(res,x,y,z) {
35360             const float
35361               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35362               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
35363               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
35364               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
35365             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1,
35366                                                              Y<height()?Y:hh - Y - 1,
35367                                                              Z<depth()?Z:dd - Z - 1,c);
35368           }
35369         } break;
35370         default : { // Nearest-neighbor interpolation
35371           const int ww = 2*width(), hh = 2*height(), dd = 2*depth();
35372           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35373           cimg_forXYZ(res,x,y,z) {
35374             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
35375             const int
35376               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
35377               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
35378               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
35379             cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1,
35380                                                     Y<height()?Y:hh - Y - 1,
35381                                                     Z<depth()?Z:dd - Z -  1,c);
35382           }
35383         }
35384         } break;
35385 
35386       case 2 : // Periodic
35387         switch (interpolation) {
35388         case 2 : { // Cubic interpolation
35389           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35390           cimg_forXYZ(res,x,y,z) {
35391             const float
35392               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35393               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35394               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35395               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35396             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_pc(X,Y,Z,c);
35397           }
35398         } break;
35399         case 1 : { // Linear interpolation
35400           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35401           cimg_forXYZ(res,x,y,z) {
35402             const float
35403               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35404               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35405               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35406               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35407             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ_p(X,Y,Z,c);
35408           }
35409         } break;
35410         default : { // Nearest-neighbor interpolation
35411           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35412           cimg_forXYZ(res,x,y,z) {
35413             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
35414             const int
35415               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()),
35416               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()),
35417               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth());
35418             cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c);
35419           }
35420         }
35421         } break;
35422 
35423       case 1 : // Neumann
35424         switch (interpolation) {
35425         case 2 : { // Cubic interpolation
35426           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35427           cimg_forXYZ(res,x,y,z) {
35428             const float
35429               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35430               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35431               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35432               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35433             cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X,Y,Z,c);
35434           }
35435         } break;
35436         case 1 : { // Linear interpolation
35437           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35438           cimg_forXYZ(res,x,y,z) {
35439             const float
35440               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35441               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35442               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35443               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35444             cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c);
35445           }
35446         } break;
35447         default : { // Nearest-neighbor interpolation
35448           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35449           cimg_forXYZ(res,x,y,z) {
35450             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
35451             const int
35452               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
35453               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
35454               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
35455             cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c);
35456           }
35457         }
35458         } break;
35459 
35460       default : // Dirichlet
35461         switch (interpolation) {
35462         case 2 : { // Cubic interpolation
35463           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35464           cimg_forXYZ(res,x,y,z) {
35465             const float
35466               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35467               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35468               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35469               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35470             cimg_forC(res,c) res(x,y,z,c) = cubic_atXYZ_c(X,Y,Z,c,(T)0);
35471           }
35472         } break;
35473         case 1 : { // Linear interpolation
35474           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35475           cimg_forXYZ(res,x,y,z) {
35476             const float
35477               xc = x - rw2, yc = y - rh2, zc = z - rd2,
35478               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
35479               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
35480               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
35481             cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0);
35482           }
35483         } break;
35484         default : { // Nearest-neighbor interpolation
35485           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
35486           cimg_forXYZ(res,x,y,z) {
35487             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
35488             const int
35489               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
35490               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
35491               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
35492             cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0);
35493           }
35494         }
35495         } break;
35496       }
35497     }
35498 
35499     //! Warp image content by a warping field.
35500     /**
35501        \param warp Warping field.
35502        \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative }
35503        \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
35504        \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
35505     **/
35506     template<typename t>
35507     CImg<T>& warp(const CImg<t>& p_warp, const unsigned int mode=0,
35508                   const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
35509       return get_warp(p_warp,mode,interpolation,boundary_conditions).move_to(*this);
35510     }
35511 
35512     //! Warp image content by a warping field \newinstance
35513     template<typename t>
35514     CImg<T> get_warp(const CImg<t>& p_warp, const unsigned int mode=0,
35515                      const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
35516       if (is_empty() || !p_warp) return *this;
35517       if (mode && !is_sameXYZ(p_warp))
35518         throw CImgArgumentException(_cimg_instance
35519                                     "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
35520                                     "have different XYZ dimensions.",
35521                                     cimg_instance,
35522                                     p_warp._width,p_warp._height,p_warp._depth,p_warp._spectrum,p_warp._data);
35523 
35524       CImg<T> res(p_warp._width,p_warp._height,p_warp._depth,_spectrum);
35525 
35526       if (p_warp._spectrum==1) { // 1D warping
35527         if (mode>=3) { // Forward-relative warp
35528           res.fill((T)0);
35529           if (interpolation>=1) // Linear interpolation
35530             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35531             cimg_forYZC(res,y,z,c) {
35532               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
35533               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c);
35534             }
35535           else // Nearest-neighbor interpolation
35536             cimg_forYZC(res,y,z,c) {
35537               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
35538               cimg_forX(res,x) {
35539                 const int X = x + (int)cimg::round(*(ptrs0++));
35540                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
35541               }
35542             }
35543         } else if (mode==2) { // Forward-absolute warp
35544           res.fill((T)0);
35545           if (interpolation>=1) // Linear interpolation
35546             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35547             cimg_forYZC(res,y,z,c) {
35548               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
35549               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c);
35550             }
35551           else // Nearest-neighbor interpolation
35552             cimg_forYZC(res,y,z,c) {
35553               const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
35554               cimg_forX(res,x) {
35555                 const int X = (int)cimg::round(*(ptrs0++));
35556                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
35557               }
35558             }
35559         } else if (mode==1) { // Backward-relative warp
35560           if (interpolation==2) // Cubic interpolation
35561             switch (boundary_conditions) {
35562             case 3 : { // Mirror
35563               const float w2 = 2.f*width();
35564               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35565               cimg_forYZC(res,y,z,c) {
35566                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35567                 cimg_forX(res,x) {
35568                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
35569                   *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,y,z,c);
35570                 }
35571               }
35572             } break;
35573             case 2 : // Periodic
35574               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35575               cimg_forYZC(res,y,z,c) {
35576                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35577                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc(x - (float)*(ptrs0++),y,z,c);
35578               }
35579               break;
35580             case 1 : // Neumann
35581               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35582               cimg_forYZC(res,y,z,c) {
35583                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35584                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_c(x - (float)*(ptrs0++),y,z,c);
35585               }
35586               break;
35587             default : // Dirichlet
35588               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35589               cimg_forYZC(res,y,z,c) {
35590                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35591                 cimg_forX(res,x) *(ptrd++) = cubic_atX_c(x - (float)*(ptrs0++),y,z,c,(T)0);
35592               }
35593             }
35594           else if (interpolation==1) // Linear interpolation
35595             switch (boundary_conditions) {
35596             case 3 : { // Mirror
35597               const float w2 = 2.f*width();
35598               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35599               cimg_forYZC(res,y,z,c) {
35600                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35601                 cimg_forX(res,x) {
35602                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
35603                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
35604                 }
35605               }
35606             } break;
35607             case 2 : // Periodic
35608               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35609               cimg_forYZC(res,y,z,c) {
35610                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35611                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p(x - (float)*(ptrs0++),y,z,c);
35612               }
35613               break;
35614             case 1 : // Neumann
35615               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35616               cimg_forYZC(res,y,z,c) {
35617                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35618                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
35619               }
35620               break;
35621             default : // Dirichlet
35622               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35623               cimg_forYZC(res,y,z,c) {
35624                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35625                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
35626               }
35627             }
35628           else // Nearest-neighbor interpolation
35629             switch (boundary_conditions) {
35630             case 3 : { // Mirror
35631               const int w2 = 2*width();
35632               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35633               cimg_forYZC(res,y,z,c) {
35634                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35635                 cimg_forX(res,x) {
35636                   const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2);
35637                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c);
35638                 }
35639               }
35640             } break;
35641             case 2 : // Periodic
35642               cimg_forYZC(res,y,z,c) {
35643                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35644                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),y,z,c);
35645               }
35646               break;
35647             case 1 : // Neumann
35648               cimg_forYZC(res,y,z,c) {
35649                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35650                 cimg_forX(res,x) *(ptrd++) = _atX(x - (int)cimg::round(*(ptrs0++)),y,z,c);
35651               }
35652               break;
35653             default : // Dirichlet
35654               cimg_forYZC(res,y,z,c) {
35655                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35656                 cimg_forX(res,x) *(ptrd++) = atX(x - (int)cimg::round(*(ptrs0++)),y,z,c,(T)0);
35657               }
35658             }
35659         }
35660         else { // Backward-absolute warp
35661           if (interpolation==2) // Cubic interpolation
35662             switch (boundary_conditions) {
35663             case 3 : { // Mirror
35664               const float w2 = 2.f*width();
35665               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35666                 cimg_forYZC(res,y,z,c) {
35667                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35668                 cimg_forX(res,x) {
35669                   const float mx = cimg::mod((float)*(ptrs0++),w2);
35670                   *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,0,0,c);
35671                 }
35672               }
35673             } break;
35674             case 2 : // Periodic
35675               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35676               cimg_forYZC(res,y,z,c) {
35677                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35678                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc((float)*(ptrs0++),0,0,c);
35679               }
35680               break;
35681             case 1 : // Neumann
35682               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35683               cimg_forYZC(res,y,z,c) {
35684                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35685                 cimg_forX(res,x) *(ptrd++) = _cubic_atX_c((float)*(ptrs0++),0,0,c);
35686               }
35687               break;
35688             default : // Dirichlet
35689               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35690               cimg_forYZC(res,y,z,c) {
35691                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35692                 cimg_forX(res,x) *(ptrd++) = cubic_atX_c((float)*(ptrs0++),0,0,c,(T)0);
35693               }
35694             }
35695           else if (interpolation==1) // Linear interpolation
35696             switch (boundary_conditions) {
35697             case 3 : { // Mirror
35698               const float w2 = 2.f*width();
35699               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35700                 cimg_forYZC(res,y,z,c) {
35701                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35702                 cimg_forX(res,x) {
35703                   const float mx = cimg::mod((float)*(ptrs0++),w2);
35704                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
35705                 }
35706               }
35707             } break;
35708             case 2 : // Periodic
35709               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35710               cimg_forYZC(res,y,z,c) {
35711                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35712                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p((float)*(ptrs0++),0,0,c);
35713               }
35714               break;
35715             case 1 : // Neumann
35716               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35717               cimg_forYZC(res,y,z,c) {
35718                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35719                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
35720               }
35721               break;
35722             default : // Dirichlet
35723               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35724               cimg_forYZC(res,y,z,c) {
35725                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35726                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0);
35727               }
35728             }
35729           else // Nearest-neighbor interpolation
35730             switch (boundary_conditions) {
35731             case 3 : { // Mirror
35732               const int w2 = 2*width();
35733               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35734                 cimg_forYZC(res,y,z,c) {
35735                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35736                 cimg_forX(res,x) {
35737                   const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2);
35738                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c);
35739                 }
35740               }
35741             } break;
35742             case 2 : // Periodic
35743               cimg_forYZC(res,y,z,c) {
35744                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35745                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),0,0,c);
35746               }
35747               break;
35748             case 1 : // Neumann
35749               cimg_forYZC(res,y,z,c) {
35750                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35751                 cimg_forX(res,x) *(ptrd++) = _atX((int)cimg::round(*(ptrs0++)),0,0,c);
35752               }
35753               break;
35754             default : // Dirichlet
35755               cimg_forYZC(res,y,z,c) {
35756                 const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
35757                 cimg_forX(res,x) *(ptrd++) = atX((int)cimg::round(*(ptrs0++)),0,0,c,(T)0);
35758               }
35759             }
35760         }
35761 
35762       } else if (p_warp._spectrum==2) { // 2D warping
35763         if (mode>=3) { // Forward-relative warp
35764           res.fill((T)0);
35765           if (interpolation>=1) // Linear interpolation
35766             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35767             cimg_forYZC(res,y,z,c) {
35768               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);
35769               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c);
35770             }
35771           else // Nearest-neighbor interpolation
35772             cimg_forYZC(res,y,z,c) {
35773               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);
35774               cimg_forX(res,x) {
35775                 const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++));
35776                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
35777               }
35778             }
35779         } else if (mode==2) { // Forward-absolute warp
35780           res.fill((T)0);
35781           if (interpolation>=1) // Linear interpolation
35782             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35783             cimg_forYZC(res,y,z,c) {
35784               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);
35785               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c);
35786             }
35787           else // Nearest-neighbor interpolation
35788             cimg_forYZC(res,y,z,c) {
35789               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);
35790               cimg_forX(res,x) {
35791                 const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++));
35792                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
35793               }
35794             }
35795         } else if (mode==1) { // Backward-relative warp
35796           if (interpolation==2) // Cubic interpolation
35797             switch (boundary_conditions) {
35798             case 3 : { // Mirror
35799               const float w2 = 2.f*width(), h2 = 2.f*height();
35800               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35801               cimg_forYZC(res,y,z,c) {
35802                 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);
35803                 cimg_forX(res,x) {
35804                   const float
35805                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
35806                     my = cimg::mod(y - (float)*(ptrs1++),h2);
35807                   *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
35808                 }
35809               }
35810             } break;
35811             case 2 : // Periodic
35812               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35813               cimg_forYZC(res,y,z,c) {
35814                 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);
35815                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
35816               }
35817               break;
35818             case 1 : // Neumann
35819               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35820               cimg_forYZC(res,y,z,c) {
35821                 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);
35822                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
35823               }
35824               break;
35825             default : // Dirichlet
35826               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35827               cimg_forYZC(res,y,z,c) {
35828                 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);
35829                 cimg_forX(res,x) *(ptrd++) = cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
35830               }
35831             }
35832           else if (interpolation==1) // Linear interpolation
35833             switch (boundary_conditions) {
35834             case 3 : { // Mirror
35835               const float w2 = 2.f*width(), h2 = 2.f*height();
35836               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35837               cimg_forYZC(res,y,z,c) {
35838                 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);
35839                 cimg_forX(res,x) {
35840                   const float
35841                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
35842                     my = cimg::mod(y - (float)*(ptrs1++),h2);
35843                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
35844                 }
35845               }
35846             } break;
35847             case 2 : // Periodic
35848               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35849               cimg_forYZC(res,y,z,c) {
35850                 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);
35851                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
35852               }
35853               break;
35854             case 1 : // Neumann
35855               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35856               cimg_forYZC(res,y,z,c) {
35857                 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);
35858                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
35859               }
35860               break;
35861             default : // Dirichlet
35862               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35863               cimg_forYZC(res,y,z,c) {
35864                 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);
35865                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
35866               }
35867             }
35868           else // Nearest-neighbor interpolation
35869             switch (boundary_conditions) {
35870             case 3 : { // Mirror
35871               const int w2 = 2*width(), h2 = 2*height();
35872               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35873               cimg_forYZC(res,y,z,c) {
35874                 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);
35875                 cimg_forX(res,x) {
35876                   const int
35877                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
35878                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2);
35879                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
35880                 }
35881               }
35882             } break;
35883             case 2 : // Periodic
35884               cimg_forYZC(res,y,z,c) {
35885                 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);
35886                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
35887                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),z,c);
35888               }
35889               break;
35890             case 1 : // Neumann
35891               cimg_forYZC(res,y,z,c) {
35892                 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);
35893                 cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)cimg::round(*(ptrs0++)),
35894                                                    y - (int)cimg::round(*(ptrs1++)),z,c);
35895               }
35896               break;
35897             default : // Dirichlet
35898               cimg_forYZC(res,y,z,c) {
35899                 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);
35900                 cimg_forX(res,x) *(ptrd++) = atXY(x - (int)cimg::round(*(ptrs0++)),
35901                                                   y - (int)cimg::round(*(ptrs1++)),z,c,(T)0);
35902               }
35903             }
35904         } else { // Backward-absolute warp
35905           if (interpolation==2) // Cubic interpolation
35906             switch (boundary_conditions) {
35907             case 3 : { // Mirror
35908               const float w2 = 2.f*width(), h2 = 2.f*height();
35909               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35910               cimg_forYZC(res,y,z,c) {
35911                 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);
35912                 cimg_forX(res,x) {
35913                   const float
35914                     mx = cimg::mod((float)*(ptrs0++),w2),
35915                     my = cimg::mod((float)*(ptrs1++),h2);
35916                   *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
35917                 }
35918               }
35919             } break;
35920             case 2 : // Periodic
35921               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35922               cimg_forYZC(res,y,z,c) {
35923                 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);
35924                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc((float)*(ptrs0++),(float)*(ptrs1++),0,c);
35925               }
35926               break;
35927             case 1 : // Neumann
35928               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35929               cimg_forYZC(res,y,z,c) {
35930                 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);
35931                 cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c);
35932               }
35933               break;
35934             default : // Dirichlet
35935               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35936               cimg_forYZC(res,y,z,c) {
35937                 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);
35938                 cimg_forX(res,x) *(ptrd++) = cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
35939               }
35940             }
35941           else if (interpolation==1) // Linear interpolation
35942             switch (boundary_conditions) {
35943             case 3 : { // Mirror
35944               const float w2 = 2.f*width(), h2 = 2.f*height();
35945               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35946               cimg_forYZC(res,y,z,c) {
35947                 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);
35948                 cimg_forX(res,x) {
35949                   const float
35950                     mx = cimg::mod((float)*(ptrs0++),w2),
35951                     my = cimg::mod((float)*(ptrs1++),h2);
35952                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
35953                 }
35954               }
35955             } break;
35956             case 2 : // Periodic
35957               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35958               cimg_forYZC(res,y,z,c) {
35959                 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);
35960                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p((float)*(ptrs0++),(float)*(ptrs1++),0,c);
35961               }
35962               break;
35963             case 1 : // Neumann
35964               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35965               cimg_forYZC(res,y,z,c) {
35966                 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);
35967                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
35968               }
35969               break;
35970             default : // Dirichlet
35971               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
35972               cimg_forYZC(res,y,z,c) {
35973                 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);
35974                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
35975               }
35976             }
35977           else // Nearest-neighbor interpolation
35978             switch (boundary_conditions) {
35979             case 3 : { // Mirror
35980               const int w2 = 2*width(), h2 = 2*height();
35981               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
35982               cimg_forYZC(res,y,z,c) {
35983                 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);
35984                 cimg_forX(res,x) {
35985                   const int
35986                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
35987                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2);
35988                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
35989                 }
35990               }
35991             } break;
35992             case 2 : // Periodic
35993               cimg_forYZC(res,y,z,c) {
35994                 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);
35995                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
35996                                                      cimg::mod((int)cimg::round(*(ptrs1++)),height()),0,c);
35997               }
35998               break;
35999             case 1 : // Neumann
36000               cimg_forYZC(res,y,z,c) {
36001                 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);
36002                 cimg_forX(res,x) *(ptrd++) = _atXY((int)cimg::round(*(ptrs0++)),
36003                                                    (int)cimg::round(*(ptrs1++)),0,c);
36004               }
36005               break;
36006             default : // Dirichlet
36007               cimg_forYZC(res,y,z,c) {
36008                 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);
36009                 cimg_forX(res,x) *(ptrd++) = atXY((int)cimg::round(*(ptrs0++)),
36010                                                   (int)cimg::round(*(ptrs1++)),0,c,(T)0);
36011               }
36012             }
36013         }
36014 
36015       } else { // 3D warping
36016         if (mode>=3) { // Forward-relative warp
36017           res.fill((T)0);
36018           if (interpolation>=1) // Linear interpolation
36019             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36020             cimg_forYZC(res,y,z,c) {
36021               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);
36022               const T *ptrs = data(0,y,z,c);
36023               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),
36024                                                     z + (float)*(ptrs2++),c);
36025             }
36026           else // Nearest-neighbor interpolation
36027             cimg_forYZC(res,y,z,c) {
36028               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);
36029               const T *ptrs = data(0,y,z,c);
36030               cimg_forX(res,x) {
36031                 const int
36032                   X = x + (int)cimg::round(*(ptrs0++)),
36033                   Y = y + (int)cimg::round(*(ptrs1++)),
36034                   Z = z + (int)cimg::round(*(ptrs2++));
36035                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
36036               }
36037             }
36038         } else if (mode==2) { // Forward-absolute warp
36039           res.fill((T)0);
36040           if (interpolation>=1) // Linear interpolation
36041             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36042             cimg_forYZC(res,y,z,c) {
36043               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);
36044               const T *ptrs = data(0,y,z,c);
36045               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36046             }
36047           else // Nearest-neighbor interpolation
36048             cimg_forYZC(res,y,z,c) {
36049               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);
36050               const T *ptrs = data(0,y,z,c);
36051               cimg_forX(res,x) {
36052                 const int
36053                   X = (int)cimg::round(*(ptrs0++)),
36054                   Y = (int)cimg::round(*(ptrs1++)),
36055                   Z = (int)cimg::round(*(ptrs2++));
36056                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
36057               }
36058             }
36059         } else if (mode==1) { // Backward-relative warp
36060           if (interpolation==2) // Cubic interpolation
36061             switch (boundary_conditions) {
36062             case 3 : { // Mirror
36063               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36064               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36065               cimg_forYZC(res,y,z,c) {
36066                 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);
36067                 T *ptrd = res.data(0,y,z,c);
36068                 cimg_forX(res,x) {
36069                   const float
36070                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36071                     my = cimg::mod(y - (float)*(ptrs1++),h2),
36072                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
36073                   *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
36074                                              my<height()?my:h2 - my - 1,
36075                                              mz<depth()?mz:d2 - mz - 1,c);
36076                 }
36077               }
36078             } break;
36079             case 2 : // Periodic
36080               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36081               cimg_forYZC(res,y,z,c) {
36082                 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);
36083                 T *ptrd = res.data(0,y,z,c);
36084                 cimg_forX(res,x)
36085                   *(ptrd++) = _cubic_atXYZ_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36086               }
36087               break;
36088             case 1 : // Neumann
36089               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36090               cimg_forYZC(res,y,z,c) {
36091                 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);
36092                 T *ptrd = res.data(0,y,z,c);
36093                 cimg_forX(res,x)
36094                   *(ptrd++) = _cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36095               }
36096               break;
36097             default : // Dirichlet
36098               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36099               cimg_forYZC(res,y,z,c) {
36100                 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);
36101                 T *ptrd = res.data(0,y,z,c);
36102                 cimg_forX(res,x)
36103                   *(ptrd++) = cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
36104               }
36105             }
36106           else if (interpolation==1) // Linear interpolation
36107             switch (boundary_conditions) {
36108             case 3 : { // Mirror
36109               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36110               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36111               cimg_forYZC(res,y,z,c) {
36112                 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);
36113                 T *ptrd = res.data(0,y,z,c);
36114                 cimg_forX(res,x) {
36115                   const float
36116                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
36117                     my = cimg::mod(y - (float)*(ptrs1++),h2),
36118                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
36119                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
36120                                                my<height()?my:h2 - my - 1,
36121                                                mz<depth()?mz:d2 - mz - 1,c);
36122                 }
36123               }
36124             } break;
36125             case 2 : // Periodic
36126               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36127               cimg_forYZC(res,y,z,c) {
36128                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36129                 T *ptrd = res.data(0,y,z,c);
36130                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),
36131                                                                 z - (float)*(ptrs2++),c);
36132               }
36133               break;
36134             case 1 : // Neumann
36135               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36136               cimg_forYZC(res,y,z,c) {
36137                 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);
36138                 T *ptrd = res.data(0,y,z,c);
36139                 cimg_forX(res,x)
36140                   *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
36141               }
36142               break;
36143             default : // Dirichlet
36144               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36145               cimg_forYZC(res,y,z,c) {
36146                 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);
36147                 T *ptrd = res.data(0,y,z,c);
36148                 cimg_forX(res,x)
36149                   *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
36150               }
36151             }
36152           else // Nearest neighbor interpolation
36153             switch (boundary_conditions) {
36154             case 3 : { // Mirror
36155               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
36156               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36157               cimg_forYZC(res,y,z,c) {
36158                 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);
36159                 T *ptrd = res.data(0,y,z,c);
36160                 cimg_forX(res,x) {
36161                   const int
36162                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
36163                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2),
36164                     mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2);
36165                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
36166                                       my<height()?my:h2 - my - 1,
36167                                       mz<depth()?mz:d2 - mz - 1,c);
36168                 }
36169               }
36170             } break;
36171             case 2 : // Periodic
36172               cimg_forYZC(res,y,z,c) {
36173                 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);
36174                 T *ptrd = res.data(0,y,z,c);
36175                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
36176                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),
36177                                                      cimg::mod(z - (int)cimg::round(*(ptrs2++)),depth()),c);
36178               }
36179               break;
36180             case 1 : // Neumann
36181               cimg_forYZC(res,y,z,c) {
36182                 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);
36183                 T *ptrd = res.data(0,y,z,c);
36184                 cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)cimg::round(*(ptrs0++)),
36185                                                     y - (int)cimg::round(*(ptrs1++)),
36186                                                     z - (int)cimg::round(*(ptrs2++)),c);
36187               }
36188               break;
36189             default : // Dirichlet
36190               cimg_forYZC(res,y,z,c) {
36191                 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);
36192                 T *ptrd = res.data(0,y,z,c);
36193                 cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)cimg::round(*(ptrs0++)),
36194                                                    y - (int)cimg::round(*(ptrs1++)),
36195                                                    z - (int)cimg::round(*(ptrs2++)),c,(T)0);
36196               }
36197             }
36198         } else { // Backward-absolute warp
36199           if (interpolation==2) // Cubic interpolation
36200             switch (boundary_conditions) {
36201             case 3 : { // Mirror
36202               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36203               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36204               cimg_forYZC(res,y,z,c) {
36205                 const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
36206                 T *ptrd = res.data(0,y,z,c);
36207                 cimg_forX(res,x) {
36208                   const float
36209                     mx = cimg::mod((float)*(ptrs0++),w2),
36210                     my = cimg::mod((float)*(ptrs1++),h2),
36211                     mz = cimg::mod((float)*(ptrs2++),d2);
36212                   *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
36213                                              my<height()?my:h2 - my - 1,
36214                                              mz<depth()?mz:d2 - mz - 1,c);
36215                 }
36216               }
36217             } break;
36218             case 2 : // Periodic
36219               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36220               cimg_forYZC(res,y,z,c) {
36221                 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);
36222                 T *ptrd = res.data(0,y,z,c);
36223                 cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_pc((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36224               }
36225               break;
36226             case 1 : // Neumann
36227               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36228               cimg_forYZC(res,y,z,c) {
36229                 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);
36230                 T *ptrd = res.data(0,y,z,c);
36231                 cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36232               }
36233               break;
36234             default : // Dirichlet
36235               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36236               cimg_forYZC(res,y,z,c) {
36237                 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);
36238                 T *ptrd = res.data(0,y,z,c);
36239                 cimg_forX(res,x) *(ptrd++) = cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
36240                                                            c,(T)0);
36241               }
36242             }
36243           else if (interpolation==1) // Linear interpolation
36244             switch (boundary_conditions) {
36245             case 3 : { // Mirror
36246               const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
36247               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36248               cimg_forYZC(res,y,z,c) {
36249                 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);
36250                 T *ptrd = res.data(0,y,z,c);
36251                 cimg_forX(res,x) {
36252                   const float
36253                     mx = cimg::mod((float)*(ptrs0++),w2),
36254                     my = cimg::mod((float)*(ptrs1++),h2),
36255                     mz = cimg::mod((float)*(ptrs2++),d2);
36256                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
36257                                                my<height()?my:h2 - my - 1,
36258                                                mz<depth()?mz:d2 - mz - 1,c);
36259                 }
36260               }
36261             } break;
36262             case 2 :// Periodic
36263               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36264               cimg_forYZC(res,y,z,c) {
36265                 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);
36266                 T *ptrd = res.data(0,y,z,c);
36267                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p((float)*(ptrs0++),(float)*(ptrs1++),
36268                                                                 (float)*(ptrs2++),c);
36269               }
36270               break;
36271             case 1 : // Neumann
36272               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36273               cimg_forYZC(res,y,z,c) {
36274                 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);
36275                 T *ptrd = res.data(0,y,z,c);
36276                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
36277               }
36278               break;
36279             default : // Dirichlet
36280               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
36281               cimg_forYZC(res,y,z,c) {
36282                 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);
36283                 T *ptrd = res.data(0,y,z,c);
36284                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
36285                                                              c,(T)0);
36286               }
36287             }
36288           else // Nearest-neighbor interpolation
36289             switch (boundary_conditions) {
36290             case 3 : { // Mirror
36291               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
36292               cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
36293               cimg_forYZC(res,y,z,c) {
36294                 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);
36295                 T *ptrd = res.data(0,y,z,c);
36296                 cimg_forX(res,x) {
36297                   const int
36298                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
36299                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2),
36300                     mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2);
36301                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
36302                                       my<height()?my:h2 - my - 1,
36303                                       mz<depth()?mz:d2 - mz - 1,c);
36304                 }
36305               }
36306             } break;
36307             case 2 : // Periodic
36308               cimg_forYZC(res,y,z,c) {
36309                 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);
36310                 T *ptrd = res.data(0,y,z,c);
36311                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
36312                                                      cimg::mod((int)cimg::round(*(ptrs1++)),height()),
36313                                                      cimg::mod((int)cimg::round(*(ptrs2++)),depth()),c);
36314               }
36315               break;
36316             case 1 : // Neumann
36317               cimg_forYZC(res,y,z,c) {
36318                 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);
36319                 T *ptrd = res.data(0,y,z,c);
36320                 cimg_forX(res,x) *(ptrd++) = _atXYZ((int)cimg::round(*(ptrs0++)),
36321                                                     (int)cimg::round(*(ptrs1++)),
36322                                                     (int)cimg::round(*(ptrs2++)),c);
36323               }
36324               break;
36325             default : // Dirichlet
36326               cimg_forYZC(res,y,z,c) {
36327                 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);
36328                 T *ptrd = res.data(0,y,z,c);
36329                 cimg_forX(res,x) *(ptrd++) = atXYZ((int)cimg::round(*(ptrs0++)),
36330                                                    (int)cimg::round(*(ptrs1++)),
36331                                                    (int)cimg::round(*(ptrs2++)),c,(T)0);
36332               }
36333             }
36334         }
36335       }
36336       return res;
36337     }
36338 
36339     //! Generate a 2D representation of a 3D image, with XY,XZ and YZ views.
36340     /**
36341        \param x0 X-coordinate of the projection point.
36342        \param y0 Y-coordinate of the projection point.
36343        \param z0 Z-coordinate of the projection point.
36344     **/
36345     CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
36346       if (is_empty() || _depth<2) return +*this;
36347       const unsigned int
36348         _x0 = (x0>=_width)?_width - 1:x0,
36349         _y0 = (y0>=_height)?_height - 1:y0,
36350         _z0 = (z0>=_depth)?_depth - 1:z0;
36351       const CImg<T>
36352         img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1),
36353         img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc").
36354         resize(_depth,_height,1,-100,-1),
36355         img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1);
36356       return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
36357         draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
36358         draw_image(0,img_xy._height,img_xz);
36359     }
36360 
36361     //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace.
36362     CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
36363       if (_depth<2) return *this;
36364       return get_projections2d(x0,y0,z0).move_to(*this);
36365     }
36366 
36367     //! Crop image region.
36368     /**
36369        \param x0 = X-coordinate of the upper-left crop rectangle corner.
36370        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
36371        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
36372        \param c0 = C-coordinate of the upper-left crop rectangle corner.
36373        \param x1 = X-coordinate of the lower-right crop rectangle corner.
36374        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
36375        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
36376        \param c1 = C-coordinate of the lower-right crop rectangle corner.
36377        \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
36378     **/
36379     CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
36380                   const int x1, const int y1, const int z1, const int c1,
36381                   const unsigned int boundary_conditions=0) {
36382       return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
36383     }
36384 
36385     //! Crop image region \newinstance.
36386     CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
36387                      const int x1, const int y1, const int z1, const int c1,
36388                      const unsigned int boundary_conditions=0) const {
36389       if (is_empty())
36390         throw CImgInstanceException(_cimg_instance
36391                                     "crop(): Empty instance.",
36392                                     cimg_instance);
36393       const int
36394         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
36395         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
36396         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
36397         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
36398       const unsigned int
36399         _boundary_conditions = nx0>=0 && nx1<width() &&
36400                                ny0>=0 && ny1<height() &&
36401                                nz0>=0 && nz1<depth() &&
36402                                nc0>=0 && nc1<spectrum()?0:boundary_conditions;
36403       CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
36404       if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum())
36405         switch (_boundary_conditions) {
36406         case 3 : { // Mirror
36407           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
36408           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
36409                                                                      _height*_depth*_spectrum>=4))
36410           cimg_forXYZC(res,x,y,z,c) {
36411             const int
36412               mx = cimg::mod(nx0 + x,w2),
36413               my = cimg::mod(ny0 + y,h2),
36414               mz = cimg::mod(nz0 + z,d2),
36415               mc = cimg::mod(nc0 + c,s2);
36416             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
36417                                    my<height()?my:h2 - my - 1,
36418                                    mz<depth()?mz:d2 - mz - 1,
36419                                    mc<spectrum()?mc:s2 - mc - 1);
36420           }
36421         } break;
36422         case 2 : { // Periodic
36423           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
36424                                                                      _height*_depth*_spectrum>=4))
36425           cimg_forXYZC(res,x,y,z,c) {
36426             res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()),
36427                                    cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum()));
36428           }
36429         } break;
36430         case 1 : // Neumann
36431           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
36432                                                                      _height*_depth*_spectrum>=4))
36433           cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c);
36434           break;
36435         default : // Dirichlet
36436           res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
36437         }
36438       else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
36439       return res;
36440     }
36441 
36442     //! Crop image region \overloading.
36443     CImg<T>& crop(const int x0, const int y0, const int z0,
36444                   const int x1, const int y1, const int z1,
36445                   const unsigned int boundary_conditions=0) {
36446       return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
36447     }
36448 
36449     //! Crop image region \newinstance.
36450     CImg<T> get_crop(const int x0, const int y0, const int z0,
36451                      const int x1, const int y1, const int z1,
36452                      const unsigned int boundary_conditions=0) const {
36453       return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
36454     }
36455 
36456     //! Crop image region \overloading.
36457     CImg<T>& crop(const int x0, const int y0,
36458                   const int x1, const int y1,
36459                   const unsigned int boundary_conditions=0) {
36460       return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
36461     }
36462 
36463     //! Crop image region \newinstance.
36464     CImg<T> get_crop(const int x0, const int y0,
36465                      const int x1, const int y1,
36466                      const unsigned int boundary_conditions=0) const {
36467       return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
36468     }
36469 
36470     //! Crop image region \overloading.
36471     CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) {
36472       return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
36473     }
36474 
36475     //! Crop image region \newinstance.
36476     CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const {
36477       return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
36478     }
36479 
36480     //! Autocrop image region, regarding the specified background value.
36481     CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
36482       if (is_empty()) return *this;
36483       for (const char *s = axes; *s; ++s) {
36484         const char axis = cimg::lowercase(*s);
36485         const CImg<intT> coords = _autocrop(value,axis);
36486         if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels
36487         else switch (axis) {
36488         case 'x' : {
36489           const int x0 = coords[0], x1 = coords[1];
36490           if (x0>=0 && x1>=0) crop(x0,x1);
36491         } break;
36492         case 'y' : {
36493           const int y0 = coords[0], y1 = coords[1];
36494           if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
36495         } break;
36496         case 'z' : {
36497           const int z0 = coords[0], z1 = coords[1];
36498           if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
36499         } break;
36500         default : {
36501           const int c0 = coords[0], c1 = coords[1];
36502           if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
36503         }
36504         }
36505       }
36506       return *this;
36507     }
36508 
36509     //! Autocrop image region, regarding the specified background value \newinstance.
36510     CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const {
36511       return (+*this).autocrop(value,axes);
36512     }
36513 
36514     //! Autocrop image region, regarding the specified background color.
36515     /**
36516        \param color Color used for the crop. If \c 0, color is guessed.
36517        \param axes Axes used for the crop.
36518     **/
36519     CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
36520       if (is_empty()) return *this;
36521       if (!color) { // Guess color
36522         const CImg<T> col1 = get_vector_at(0,0,0);
36523         const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
36524         autocrop(col1,axes);
36525         if (_width==w && _height==h && _depth==d && _spectrum==s) {
36526           const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1);
36527           autocrop(col2,axes);
36528         }
36529         return *this;
36530       }
36531       for (const char *s = axes; *s; ++s) {
36532         const char axis = cimg::lowercase(*s);
36533         switch (axis) {
36534         case 'x' : {
36535           int x0 = width(), x1 = -1;
36536           cimg_forC(*this,c) {
36537             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
36538             const int nx0 = coords[0], nx1 = coords[1];
36539             if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); }
36540           }
36541           if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
36542         } break;
36543         case 'y' : {
36544           int y0 = height(), y1 = -1;
36545           cimg_forC(*this,c) {
36546             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
36547             const int ny0 = coords[0], ny1 = coords[1];
36548             if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); }
36549           }
36550           if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1);
36551         } break;
36552         default : {
36553           int z0 = depth(), z1 = -1;
36554           cimg_forC(*this,c) {
36555             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
36556             const int nz0 = coords[0], nz1 = coords[1];
36557             if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); }
36558           }
36559           if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1);
36560         }
36561         }
36562       }
36563       return *this;
36564     }
36565 
36566     //! Autocrop image region, regarding the specified background color \newinstance.
36567     CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
36568       return (+*this).autocrop(color,axes);
36569     }
36570 
36571     CImg<intT> _autocrop(const T& value, const char axis) const {
36572       CImg<intT> res;
36573       switch (cimg::lowercase(axis)) {
36574       case 'x' : {
36575         int x0 = -1, x1 = -1;
36576         cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
36577           if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
36578         if (x0>=0) {
36579           for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c)
36580             if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
36581         }
36582         res = CImg<intT>::vector(x0,x1);
36583       } break;
36584       case 'y' : {
36585         int y0 = -1, y1 = -1;
36586         cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
36587           if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
36588         if (y0>=0) {
36589           for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c)
36590             if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
36591         }
36592         res = CImg<intT>::vector(y0,y1);
36593       } break;
36594       case 'z' : {
36595         int z0 = -1, z1 = -1;
36596         cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
36597           if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
36598         if (z0>=0) {
36599           for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c)
36600             if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
36601         }
36602         res = CImg<intT>::vector(z0,z1);
36603       } break;
36604       default : {
36605         int c0 = -1, c1 = -1;
36606         cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
36607           if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
36608         if (c0>=0) {
36609           for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
36610             if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
36611         }
36612         res = CImg<intT>::vector(c0,c1);
36613       }
36614       }
36615       return res;
36616     }
36617 
36618     //! Return specified image column.
36619     /**
36620        \param x0 Image column.
36621     **/
36622     CImg<T> get_column(const int x0) const {
36623       return get_columns(x0,x0);
36624     }
36625 
36626     //! Return specified image column \inplace.
36627     CImg<T>& column(const int x0) {
36628       return columns(x0,x0);
36629     }
36630 
36631     //! Return specified range of image columns.
36632     /**
36633        \param x0 Starting image column.
36634        \param x1 Ending image column.
36635     **/
36636     CImg<T>& columns(const int x0, const int x1) {
36637       return get_columns(x0,x1).move_to(*this);
36638     }
36639 
36640     //! Return specified range of image columns \inplace.
36641     CImg<T> get_columns(const int x0, const int x1) const {
36642       return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);
36643     }
36644 
36645     //! Return specified image row.
36646     CImg<T> get_row(const int y0) const {
36647       return get_rows(y0,y0);
36648     }
36649 
36650     //! Return specified image row \inplace.
36651     /**
36652        \param y0 Image row.
36653     **/
36654     CImg<T>& row(const int y0) {
36655       return rows(y0,y0);
36656     }
36657 
36658     //! Return specified range of image rows.
36659     /**
36660        \param y0 Starting image row.
36661        \param y1 Ending image row.
36662     **/
36663     CImg<T> get_rows(const int y0, const int y1) const {
36664       return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1);
36665     }
36666 
36667     //! Return specified range of image rows \inplace.
36668     CImg<T>& rows(const int y0, const int y1) {
36669       return get_rows(y0,y1).move_to(*this);
36670     }
36671 
36672     //! Return specified image slice.
36673     /**
36674        \param z0 Image slice.
36675     **/
36676     CImg<T> get_slice(const int z0) const {
36677       return get_slices(z0,z0);
36678     }
36679 
36680     //! Return specified image slice \inplace.
36681     CImg<T>& slice(const int z0) {
36682       return slices(z0,z0);
36683     }
36684 
36685     //! Return specified range of image slices.
36686     /**
36687        \param z0 Starting image slice.
36688        \param z1 Ending image slice.
36689     **/
36690     CImg<T> get_slices(const int z0, const int z1) const {
36691       return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
36692     }
36693 
36694     //! Return specified range of image slices \inplace.
36695     CImg<T>& slices(const int z0, const int z1) {
36696       return get_slices(z0,z1).move_to(*this);
36697     }
36698 
36699     //! Return specified image channel.
36700     /**
36701        \param c0 Image channel.
36702     **/
36703     CImg<T> get_channel(const int c0) const {
36704       return get_channels(c0,c0);
36705     }
36706 
36707     //! Return specified image channel \inplace.
36708     CImg<T>& channel(const int c0) {
36709       return channels(c0,c0);
36710     }
36711 
36712     //! Return specified range of image channels.
36713     /**
36714        \param c0 Starting image channel.
36715        \param c1 Ending image channel.
36716     **/
36717     CImg<T> get_channels(const int c0, const int c1) const {
36718       return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1);
36719     }
36720 
36721     //! Return specified range of image channels \inplace.
36722     CImg<T>& channels(const int c0, const int c1) {
36723       return get_channels(c0,c1).move_to(*this);
36724     }
36725 
36726     //! Return stream line of a 2D or 3D vector field.
36727     CImg<floatT> get_streamline(const float x, const float y, const float z,
36728                                 const float L=256, const float dl=0.1f,
36729                                 const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
36730                                 const bool is_oriented_only=false) const {
36731       if (_spectrum!=2 && _spectrum!=3)
36732         throw CImgInstanceException(_cimg_instance
36733                                     "streamline(): Instance is not a 2D or 3D vector field.",
36734                                     cimg_instance);
36735       if (_spectrum==2) {
36736         if (is_oriented_only) {
36737           typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
36738           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
36739                             0,0,0,_width - 1.f,_height - 1.f,0.f);
36740         } else {
36741           typename CImg<T>::_functor4d_streamline2d_directed func(*this);
36742           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
36743                             0,0,0,_width - 1.f,_height - 1.f,0.f);
36744         }
36745       }
36746       if (is_oriented_only) {
36747         typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
36748         return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
36749                           0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
36750       }
36751       typename CImg<T>::_functor4d_streamline3d_directed func(*this);
36752       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
36753                         0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
36754     }
36755 
36756     //! Return stream line of a 3D vector field.
36757     /**
36758        \param func Vector field function.
36759        \param x X-coordinate of the starting point of the streamline.
36760        \param y Y-coordinate of the starting point of the streamline.
36761        \param z Z-coordinate of the starting point of the streamline.
36762        \param L Streamline length.
36763        \param dl Streamline length increment.
36764        \param interpolation_type Type of interpolation.
36765          Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
36766        \param is_backward_tracking Tells if the streamline is estimated forward or backward.
36767        \param is_oriented_only Tells if the direction of the vectors must be ignored.
36768        \param x0 X-coordinate of the first bounding-box vertex.
36769        \param y0 Y-coordinate of the first bounding-box vertex.
36770        \param z0 Z-coordinate of the first bounding-box vertex.
36771        \param x1 X-coordinate of the second bounding-box vertex.
36772        \param y1 Y-coordinate of the second bounding-box vertex.
36773        \param z1 Z-coordinate of the second bounding-box vertex.
36774     **/
36775     template<typename tfunc>
36776     static CImg<floatT> streamline(const tfunc& func,
36777                                    const float x, const float y, const float z,
36778                                    const float L=256, const float dl=0.1f,
36779                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
36780                                    const bool is_oriented_only=false,
36781                                    const float x0=0, const float y0=0, const float z0=0,
36782                                    const float x1=0, const float y1=0, const float z1=0) {
36783       if (dl<=0)
36784         throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
36785                                     "(should be >0).",
36786                                     pixel_type(),
36787                                     dl);
36788 
36789       const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
36790       if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
36791       const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
36792       CImg<floatT> coordinates(size_L,3);
36793       const float dl2 = dl/2;
36794       float
36795         *ptr_x = coordinates.data(0,0),
36796         *ptr_y = coordinates.data(0,1),
36797         *ptr_z = coordinates.data(0,2),
36798         pu = (float)(dl*func(x,y,z,0)),
36799         pv = (float)(dl*func(x,y,z,1)),
36800         pw = (float)(dl*func(x,y,z,2)),
36801         X = x, Y = y, Z = z;
36802 
36803       switch (interpolation_type) {
36804       case 0 : { // Nearest integer interpolation
36805         cimg_forX(coordinates,l) {
36806           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
36807           const int
36808             xi = (int)(X>0?X + 0.5f:X - 0.5f),
36809             yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
36810             zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
36811           float
36812             u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
36813             v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
36814             w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
36815           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
36816           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
36817           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
36818         }
36819       } break;
36820       case 1 : { // First-order interpolation
36821         cimg_forX(coordinates,l) {
36822           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
36823           float
36824             u = (float)(dl*func(X,Y,Z,0)),
36825             v = (float)(dl*func(X,Y,Z,1)),
36826             w = (float)(dl*func(X,Y,Z,2));
36827           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
36828           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
36829           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
36830         }
36831       } break;
36832       case 2 : { // Second order interpolation
36833         cimg_forX(coordinates,l) {
36834           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
36835           float
36836             u0 = (float)(dl2*func(X,Y,Z,0)),
36837             v0 = (float)(dl2*func(X,Y,Z,1)),
36838             w0 = (float)(dl2*func(X,Y,Z,2));
36839           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
36840           float
36841             u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
36842             v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
36843             w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
36844           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
36845           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
36846           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
36847         }
36848       } break;
36849       default : { // Fourth order interpolation
36850         cimg_forX(coordinates,k) {
36851           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
36852           float
36853             u0 = (float)(dl2*func(X,Y,Z,0)),
36854             v0 = (float)(dl2*func(X,Y,Z,1)),
36855             w0 = (float)(dl2*func(X,Y,Z,2));
36856           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
36857           float
36858             u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
36859             v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
36860             w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
36861           if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
36862           float
36863             u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
36864             v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
36865             w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
36866           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
36867           float
36868             u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
36869             v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
36870             w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
36871           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
36872           const float
36873             u = (u0 + u3)/3 + (u1 + u2)/1.5f,
36874             v = (v0 + v3)/3 + (v1 + v2)/1.5f,
36875             w = (w0 + w3)/3 + (w1 + w2)/1.5f;
36876           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
36877           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
36878         }
36879       }
36880       }
36881       if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
36882       return coordinates;
36883     }
36884 
36885     //! Return stream line of a 3D vector field \overloading.
36886     static CImg<floatT> streamline(const char *const expression,
36887                                    const float x, const float y, const float z,
36888                                    const float L=256, const float dl=0.1f,
36889                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
36890                                    const bool is_oriented_only=false,
36891                                    const float x0=0, const float y0=0, const float z0=0,
36892                                    const float x1=0, const float y1=0, const float z1=0) {
36893       _functor4d_streamline_expr func(expression);
36894       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
36895     }
36896 
36897     struct _functor4d_streamline2d_directed {
36898       const CImg<T>& ref;
36899       _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
36900       float operator()(const float x, const float y, const float z, const unsigned int c) const {
36901         return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
36902       }
36903     };
36904 
36905     struct _functor4d_streamline3d_directed {
36906       const CImg<T>& ref;
36907       _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
36908       float operator()(const float x, const float y, const float z, const unsigned int c) const {
36909         return (float)ref._linear_atXYZ(x,y,z,c);
36910       }
36911     };
36912 
36913     struct _functor4d_streamline2d_oriented {
36914       const CImg<T>& ref;
36915       CImg<floatT> *pI;
36916       _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
36917       ~_functor4d_streamline2d_oriented() { delete pI; }
36918       float operator()(const float x, const float y, const float z, const unsigned int c) const {
36919 #define _cimg_vecalign2d(i,j) \
36920         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); }
36921         int
36922           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
36923           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
36924           zi = (int)z;
36925         const float
36926           dx = x - xi,
36927           dy = y - yi;
36928         if (c==0) {
36929           CImg<floatT>& I = *pI;
36930           if (xi<0) xi = 0;
36931           if (nxi<0) nxi = 0;
36932           if (xi>=ref.width()) xi = ref.width() - 1;
36933           if (nxi>=ref.width()) nxi = ref.width() - 1;
36934           if (yi<0) yi = 0;
36935           if (nyi<0) nyi = 0;
36936           if (yi>=ref.height()) yi = ref.height() - 1;
36937           if (nyi>=ref.height()) nyi = ref.height() - 1;
36938           I(0,0,0) = (float)ref(xi,yi,zi,0);   I(0,0,1) = (float)ref(xi,yi,zi,1);
36939           I(1,0,0) = (float)ref(nxi,yi,zi,0);  I(1,0,1) = (float)ref(nxi,yi,zi,1);
36940           I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
36941           I(0,1,0) = (float)ref(xi,nyi,zi,0);  I(0,1,1) = (float)ref(xi,nyi,zi,1);
36942           _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
36943         }
36944         return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
36945       }
36946     };
36947 
36948     struct _functor4d_streamline3d_oriented {
36949       const CImg<T>& ref;
36950       CImg<floatT> *pI;
36951       _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
36952       ~_functor4d_streamline3d_oriented() { delete pI; }
36953       float operator()(const float x, const float y, const float z, const unsigned int c) const {
36954 #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) { \
36955   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); }
36956         int
36957           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
36958           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
36959           zi = (int)z - (z>=0?0:1), nzi = zi + 1;
36960         const float
36961           dx = x - xi,
36962           dy = y - yi,
36963           dz = z - zi;
36964         if (c==0) {
36965           CImg<floatT>& I = *pI;
36966           if (xi<0) xi = 0;
36967           if (nxi<0) nxi = 0;
36968           if (xi>=ref.width()) xi = ref.width() - 1;
36969           if (nxi>=ref.width()) nxi = ref.width() - 1;
36970           if (yi<0) yi = 0;
36971           if (nyi<0) nyi = 0;
36972           if (yi>=ref.height()) yi = ref.height() - 1;
36973           if (nyi>=ref.height()) nyi = ref.height() - 1;
36974           if (zi<0) zi = 0;
36975           if (nzi<0) nzi = 0;
36976           if (zi>=ref.depth()) zi = ref.depth() - 1;
36977           if (nzi>=ref.depth()) nzi = ref.depth() - 1;
36978           I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
36979           I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
36980           I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
36981           I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
36982           I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
36983           I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
36984           I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
36985           I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
36986           I(1,0,1,1) = (float)ref(nxi,yi,nzi,1);  I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
36987           I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
36988           I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
36989           I(0,1,1,1) = (float)ref(xi,nyi,nzi,1);  I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
36990           _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
36991           _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
36992         }
36993         return (float)pI->_linear_atXYZ(dx,dy,dz,c);
36994       }
36995     };
36996 
36997     struct _functor4d_streamline_expr {
36998       _cimg_math_parser *mp;
36999       ~_functor4d_streamline_expr() { mp->end(); delete mp; }
37000       _functor4d_streamline_expr(const char *const expr):mp(0) {
37001         mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
37002       }
37003       float operator()(const float x, const float y, const float z, const unsigned int c) const {
37004         return (float)(*mp)(x,y,z,c);
37005       }
37006     };
37007 
37008     //! Return a shared-memory image referencing a range of pixels of the image instance.
37009     /**
37010        \param x0 X-coordinate of the starting pixel.
37011        \param x1 X-coordinate of the ending pixel.
37012        \param y0 Y-coordinate.
37013        \param z0 Z-coordinate.
37014        \param c0 C-coordinate.
37015      **/
37016     CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
37017                               const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
37018       const ulongT
37019         beg = (ulongT)offset(x0,y0,z0,c0),
37020         end = (ulongT)offset(x1,y0,z0,c0);
37021       if (beg>end || beg>=size() || end>=size())
37022         throw CImgArgumentException(_cimg_instance
37023                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
37024                                     cimg_instance,
37025                                     x0,x1,y0,z0,c0);
37026       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
37027     }
37028 
37029     //! Return a shared-memory image referencing a range of pixels of the image instance \const.
37030     const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
37031                                     const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
37032       const ulongT
37033         beg = (ulongT)offset(x0,y0,z0,c0),
37034         end = (ulongT)offset(x1,y0,z0,c0);
37035       if (beg>end || beg>=size() || end>=size())
37036         throw CImgArgumentException(_cimg_instance
37037                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
37038                                     cimg_instance,
37039                                     x0,x1,y0,z0,c0);
37040       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
37041     }
37042 
37043     //! Return a shared-memory image referencing a range of rows of the image instance.
37044     /**
37045        \param y0 Y-coordinate of the starting row.
37046        \param y1 Y-coordinate of the ending row.
37047        \param z0 Z-coordinate.
37048        \param c0 C-coordinate.
37049     **/
37050     CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
37051                              const unsigned int z0=0, const unsigned int c0=0) {
37052       const ulongT
37053         beg = (ulongT)offset(0,y0,z0,c0),
37054         end = (ulongT)offset(0,y1,z0,c0);
37055       if (beg>end || beg>=size() || end>=size())
37056         throw CImgArgumentException(_cimg_instance
37057                                     "get_shared_rows(): Invalid request of a shared-memory subset "
37058                                     "(0->%u,%u->%u,%u,%u).",
37059                                     cimg_instance,
37060                                     _width - 1,y0,y1,z0,c0);
37061       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
37062     }
37063 
37064     //! Return a shared-memory image referencing a range of rows of the image instance \const.
37065     const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
37066                                    const unsigned int z0=0, const unsigned int c0=0) const {
37067       const ulongT
37068         beg = (ulongT)offset(0,y0,z0,c0),
37069         end = (ulongT)offset(0,y1,z0,c0);
37070       if (beg>end || beg>=size() || end>=size())
37071         throw CImgArgumentException(_cimg_instance
37072                                     "get_shared_rows(): Invalid request of a shared-memory subset "
37073                                     "(0->%u,%u->%u,%u,%u).",
37074                                     cimg_instance,
37075                                     _width - 1,y0,y1,z0,c0);
37076       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
37077     }
37078 
37079     //! Return a shared-memory image referencing one row of the image instance.
37080     /**
37081        \param y0 Y-coordinate.
37082        \param z0 Z-coordinate.
37083        \param c0 C-coordinate.
37084     **/
37085     CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
37086       return get_shared_rows(y0,y0,z0,c0);
37087     }
37088 
37089     //! Return a shared-memory image referencing one row of the image instance \const.
37090     const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
37091       return get_shared_rows(y0,y0,z0,c0);
37092     }
37093 
37094     //! Return a shared memory image referencing a range of slices of the image instance.
37095     /**
37096        \param z0 Z-coordinate of the starting slice.
37097        \param z1 Z-coordinate of the ending slice.
37098        \param c0 C-coordinate.
37099     **/
37100     CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
37101       const ulongT
37102         beg = (ulongT)offset(0,0,z0,c0),
37103         end = (ulongT)offset(0,0,z1,c0);
37104       if (beg>end || beg>=size() || end>=size())
37105         throw CImgArgumentException(_cimg_instance
37106                                     "get_shared_slices(): Invalid request of a shared-memory subset "
37107                                     "(0->%u,0->%u,%u->%u,%u).",
37108                                     cimg_instance,
37109                                     _width - 1,_height - 1,z0,z1,c0);
37110       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
37111     }
37112 
37113     //! Return a shared memory image referencing a range of slices of the image instance \const.
37114     const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
37115       const ulongT
37116         beg = (ulongT)offset(0,0,z0,c0),
37117         end = (ulongT)offset(0,0,z1,c0);
37118       if (beg>end || beg>=size() || end>=size())
37119         throw CImgArgumentException(_cimg_instance
37120                                     "get_shared_slices(): Invalid request of a shared-memory subset "
37121                                     "(0->%u,0->%u,%u->%u,%u).",
37122                                     cimg_instance,
37123                                     _width - 1,_height - 1,z0,z1,c0);
37124       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
37125     }
37126 
37127     //! Return a shared-memory image referencing one slice of the image instance.
37128     /**
37129        \param z0 Z-coordinate.
37130        \param c0 C-coordinate.
37131     **/
37132     CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
37133       return get_shared_slices(z0,z0,c0);
37134     }
37135 
37136     //! Return a shared-memory image referencing one slice of the image instance \const.
37137     const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
37138       return get_shared_slices(z0,z0,c0);
37139     }
37140 
37141     //! Return a shared-memory image referencing a range of channels of the image instance.
37142     /**
37143        \param c0 C-coordinate of the starting channel.
37144        \param c1 C-coordinate of the ending channel.
37145     **/
37146     CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
37147       const ulongT
37148         beg = (ulongT)offset(0,0,0,c0),
37149         end = (ulongT)offset(0,0,0,c1);
37150       if (beg>end || beg>=size() || end>=size())
37151         throw CImgArgumentException(_cimg_instance
37152                                     "get_shared_channels(): Invalid request of a shared-memory subset "
37153                                     "(0->%u,0->%u,0->%u,%u->%u).",
37154                                     cimg_instance,
37155                                     _width - 1,_height - 1,_depth - 1,c0,c1);
37156       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
37157     }
37158 
37159     //! Return a shared-memory image referencing a range of channels of the image instance \const.
37160     const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
37161       const ulongT
37162         beg = (ulongT)offset(0,0,0,c0),
37163         end = (ulongT)offset(0,0,0,c1);
37164       if (beg>end || beg>=size() || end>=size())
37165         throw CImgArgumentException(_cimg_instance
37166                                     "get_shared_channels(): Invalid request of a shared-memory subset "
37167                                     "(0->%u,0->%u,0->%u,%u->%u).",
37168                                     cimg_instance,
37169                                     _width - 1,_height - 1,_depth - 1,c0,c1);
37170       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
37171     }
37172 
37173     //! Return a shared-memory image referencing one channel of the image instance.
37174     /**
37175        \param c0 C-coordinate.
37176     **/
37177     CImg<T> get_shared_channel(const unsigned int c0) {
37178       return get_shared_channels(c0,c0);
37179     }
37180 
37181     //! Return a shared-memory image referencing one channel of the image instance \const.
37182     const CImg<T> get_shared_channel(const unsigned int c0) const {
37183       return get_shared_channels(c0,c0);
37184     }
37185 
37186     //! Return a shared-memory version of the image instance.
37187     CImg<T> get_shared() {
37188       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
37189     }
37190 
37191     //! Return a shared-memory version of the image instance \const.
37192     const CImg<T> get_shared() const {
37193       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
37194     }
37195 
37196     //! Split image into a list along specified axis.
37197     /**
37198        \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
37199        \param nb Number of split parts.
37200        \note
37201        - If \c nb==0, instance image is split into blocs of egal values along the specified axis.
37202        - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide.
37203        - If \c nb>0, instance image is split into \c nb blocs.
37204     **/
37205     CImgList<T> get_split(const char axis, const int nb=-1) const {
37206       CImgList<T> res;
37207       if (is_empty()) return res;
37208       const char _axis = cimg::lowercase(axis);
37209 
37210       if (nb<0) { // Split by bloc size
37211         const unsigned int dp = (unsigned int)(nb?-nb:1);
37212         switch (_axis) {
37213         case 'x': {
37214           if (_width>dp) {
37215             res.assign(_width/dp + (_width%dp?1:0),1,1);
37216             const unsigned int pe = _width - dp;
37217             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37218                                                            _height*_depth*_spectrum>=128))
37219             for (int p = 0; p<(int)pe; p+=dp)
37220               get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
37221             get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37222           } else res.assign(*this);
37223         } break;
37224         case 'y': {
37225           if (_height>dp) {
37226             res.assign(_height/dp + (_height%dp?1:0),1,1);
37227             const unsigned int pe = _height - dp;
37228             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37229                                                            _width*_depth*_spectrum>=128))
37230             for (int p = 0; p<(int)pe; p+=dp)
37231               get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
37232             get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37233           } else res.assign(*this);
37234         } break;
37235         case 'z': {
37236           if (_depth>dp) {
37237             res.assign(_depth/dp + (_depth%dp?1:0),1,1);
37238             const unsigned int pe = _depth - dp;
37239             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37240                                                            _width*_height*_spectrum>=128))
37241             for (int p = 0; p<(int)pe; p+=dp)
37242               get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]);
37243             get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37244           } else res.assign(*this);
37245         } break;
37246         case 'c' : {
37247           if (_spectrum>dp) {
37248             res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1);
37249             const unsigned int pe = _spectrum - dp;
37250             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
37251                                                            _width*_height*_depth>=128))
37252             for (int p = 0; p<(int)pe; p+=dp)
37253               get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]);
37254             get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
37255           } else res.assign(*this);
37256         }
37257         }
37258       } else if (nb>0) { // Split by number of (non-homogeneous) blocs
37259         const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
37260         if ((unsigned int)nb>siz)
37261           throw CImgArgumentException(_cimg_instance
37262                                       "get_split(): Instance cannot be split along %c-axis into %u blocs.",
37263                                       cimg_instance,
37264                                       axis,nb);
37265         if (nb==1) res.assign(*this);
37266         else {
37267           int err = (int)siz;
37268           unsigned int _p = 0;
37269           switch (_axis) {
37270           case 'x' : {
37271             cimg_forX(*this,p) if ((err-=nb)<=0) {
37272               get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res);
37273               err+=(int)siz;
37274               _p = p + 1U;
37275             }
37276           } break;
37277           case 'y' : {
37278             cimg_forY(*this,p) if ((err-=nb)<=0) {
37279               get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res);
37280               err+=(int)siz;
37281               _p = p + 1U;
37282             }
37283           } break;
37284           case 'z' : {
37285             cimg_forZ(*this,p) if ((err-=nb)<=0) {
37286               get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res);
37287               err+=(int)siz;
37288               _p = p + 1U;
37289             }
37290           } break;
37291           case 'c' : {
37292             cimg_forC(*this,p) if ((err-=nb)<=0) {
37293               get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res);
37294               err+=(int)siz;
37295               _p = p + 1U;
37296             }
37297           }
37298           }
37299         }
37300       } else { // Split by egal values according to specified axis
37301         T current = *_data;
37302         switch (_axis) {
37303         case 'x' : {
37304           int i0 = 0;
37305           cimg_forX(*this,i)
37306             if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); }
37307           get_columns(i0,width() - 1).move_to(res);
37308         } break;
37309         case 'y' : {
37310           int i0 = 0;
37311           cimg_forY(*this,i)
37312             if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); }
37313           get_rows(i0,height() - 1).move_to(res);
37314         } break;
37315         case 'z' : {
37316           int i0 = 0;
37317           cimg_forZ(*this,i)
37318             if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); }
37319           get_slices(i0,depth() - 1).move_to(res);
37320         } break;
37321         case 'c' : {
37322           int i0 = 0;
37323           cimg_forC(*this,i)
37324             if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); }
37325           get_channels(i0,spectrum() - 1).move_to(res);
37326         } break;
37327         default : {
37328           longT i0 = 0;
37329           cimg_foroff(*this,i)
37330             if ((*this)[i]!=current) {
37331               CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
37332               i0 = (longT)i; current = (*this)[i];
37333             }
37334           CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res);
37335         }
37336         }
37337       }
37338       return res;
37339     }
37340 
37341     //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis.
37342     /**
37343        \param values Splitting value sequence.
37344        \param axis Axis along which the splitting is performed. Can be '0' to ignore axis.
37345        \param keep_values Tells if the splitting sequence must be kept in the split blocs.
37346      **/
37347     template<typename t>
37348     CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const {
37349       typedef _cimg_Tt Tt;
37350 
37351       CImgList<T> res;
37352       if (is_empty()) return res;
37353       const ulongT vsiz = values.size();
37354       const char _axis = cimg::lowercase(axis);
37355       if (!vsiz) return CImgList<T>(*this);
37356       if (vsiz==1) { // Split according to a single value
37357         const T value = (T)*values;
37358         switch (_axis) {
37359         case 'x' : {
37360           unsigned int i0 = 0, i = 0;
37361           do {
37362             while (i<_width && (*this)(i)==value) ++i;
37363             if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; }
37364             while (i<_width && (*this)(i)!=value) ++i;
37365             if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; }
37366           } while (i<_width);
37367         } break;
37368         case 'y' : {
37369           unsigned int i0 = 0, i = 0;
37370           do {
37371             while (i<_height && (*this)(0,i)==value) ++i;
37372             if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; }
37373             while (i<_height && (*this)(0,i)!=value) ++i;
37374             if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; }
37375           } while (i<_height);
37376         } break;
37377         case 'z' : {
37378           unsigned int i0 = 0, i = 0;
37379           do {
37380             while (i<_depth && (*this)(0,0,i)==value) ++i;
37381             if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; }
37382             while (i<_depth && (*this)(0,0,i)!=value) ++i;
37383             if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; }
37384           } while (i<_depth);
37385         } break;
37386         case 'c' : {
37387           unsigned int i0 = 0, i = 0;
37388           do {
37389             while (i<_spectrum && (*this)(0,0,0,i)==value) ++i;
37390             if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; }
37391             while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i;
37392             if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; }
37393           } while (i<_spectrum);
37394         } break;
37395         default : {
37396           const ulongT siz = size();
37397           ulongT i0 = 0, i = 0;
37398           do {
37399             while (i<siz && (*this)[i]==value) ++i;
37400             if (i>i0) {
37401               if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
37402               i0 = i;
37403             }
37404             while (i<siz && (*this)[i]!=value) ++i;
37405             if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
37406           } while (i<siz);
37407         }
37408         }
37409       } else { // Split according to multiple values
37410         ulongT j = 0;
37411         switch (_axis) {
37412         case 'x' : {
37413           unsigned int i0 = 0, i1 = 0, i = 0;
37414           do {
37415             if ((Tt)(*this)(i)==(Tt)*values) {
37416               i1 = i; j = 0;
37417               while (i<_width && (Tt)(*this)(i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37418               i-=j;
37419               if (i>i1) {
37420                 if (i1>i0) get_columns(i0,i1 - 1).move_to(res);
37421                 if (keep_values) get_columns(i1,i - 1).move_to(res);
37422                 i0 = i;
37423               } else ++i;
37424             } else ++i;
37425           } while (i<_width);
37426           if (i0<_width) get_columns(i0,width() - 1).move_to(res);
37427         } break;
37428         case 'y' : {
37429           unsigned int i0 = 0, i1 = 0, i = 0;
37430           do {
37431             if ((Tt)(*this)(0,i)==(Tt)*values) {
37432               i1 = i; j = 0;
37433               while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37434               i-=j;
37435               if (i>i1) {
37436                 if (i1>i0) get_rows(i0,i1 - 1).move_to(res);
37437                 if (keep_values) get_rows(i1,i - 1).move_to(res);
37438                 i0 = i;
37439               } else ++i;
37440             } else ++i;
37441           } while (i<_height);
37442           if (i0<_height) get_rows(i0,height() - 1).move_to(res);
37443         } break;
37444         case 'z' : {
37445           unsigned int i0 = 0, i1 = 0, i = 0;
37446           do {
37447             if ((Tt)(*this)(0,0,i)==(Tt)*values) {
37448               i1 = i; j = 0;
37449               while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37450               i-=j;
37451               if (i>i1) {
37452                 if (i1>i0) get_slices(i0,i1 - 1).move_to(res);
37453                 if (keep_values) get_slices(i1,i - 1).move_to(res);
37454                 i0 = i;
37455               } else ++i;
37456             } else ++i;
37457           } while (i<_depth);
37458           if (i0<_depth) get_slices(i0,depth() - 1).move_to(res);
37459         } break;
37460         case 'c' : {
37461           unsigned int i0 = 0, i1 = 0, i = 0;
37462           do {
37463             if ((Tt)(*this)(0,0,0,i)==(Tt)*values) {
37464               i1 = i; j = 0;
37465               while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37466               i-=j;
37467               if (i>i1) {
37468                 if (i1>i0) get_channels(i0,i1 - 1).move_to(res);
37469                 if (keep_values) get_channels(i1,i - 1).move_to(res);
37470                 i0 = i;
37471               } else ++i;
37472             } else ++i;
37473           } while (i<_spectrum);
37474           if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res);
37475         } break;
37476         default : {
37477           const ulongT siz = size();
37478           ulongT i0 = 0, i1 = 0, i = 0;
37479           do {
37480             if ((Tt)(*this)[i]==(Tt)*values) {
37481               i1 = i; j = 0;
37482               while (i<siz && (Tt)(*this)[i]==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
37483               i-=j;
37484               if (i>i1) {
37485                 if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res);
37486                 if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res);
37487                 i0 = i;
37488               } else ++i;
37489             } else ++i;
37490           } while (i<siz);
37491           if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res);
37492         } break;
37493         }
37494       }
37495       return res;
37496     }
37497 
37498     //! Append two images along specified axis.
37499     /**
37500        \param img Image to append with instance image.
37501        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
37502        \param align Append alignment in \c [0,1].
37503     **/
37504     template<typename t>
37505     CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
37506       if (is_empty()) return assign(img,false);
37507       if (!img) return *this;
37508       return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
37509     }
37510 
37511     //! Append two images along specified axis \specialization.
37512     CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
37513       if (is_empty()) return assign(img,false);
37514       if (!img) return *this;
37515       return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
37516     }
37517 
37518     //! Append two images along specified axis \const.
37519     template<typename t>
37520     CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
37521       if (is_empty()) return +img;
37522       if (!img) return +*this;
37523       return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
37524     }
37525 
37526     //! Append two images along specified axis \specialization.
37527     CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
37528       if (is_empty()) return +img;
37529       if (!img) return +*this;
37530       return CImgList<T>(*this,img,true).get_append(axis,align);
37531     }
37532 
37533     //@}
37534     //---------------------------------------
37535     //
37536     //! \name Filtering / Transforms
37537     //@{
37538     //---------------------------------------
37539 
37540     //! Correlate image by a kernel.
37541     /**
37542        \param kernel = the correlation kernel.
37543        \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
37544        \param is_normalized = enable local normalization.
37545        \param channel mode Channel processing mode. Can be { 0=sum inputs | 1=one-for-one | 2=expand }
37546        \param xcenter X-coordinate of the kernel center (~0U means 'centered').
37547        \param xstart Starting X-coordinate of the instance image.
37548        \param xend Ending X-coordinate of the instance image.
37549        \param xstride Stride along the X-axis.
37550        \param xdilation Dilation along the X-axis.
37551        \param ycenter Y-coordinate of the kernel center (~0U means 'centered').
37552        \param ystart Starting Y-coordinate of the instance image.
37553        \param yend Ending Y-coordinate of the instance image.
37554        \param ystride Stride along the Y-axis.
37555        \param ydilation Dilation along the Y-axis.
37556        \param zcenter Z-coordinate of the kernel center (~0U means 'centered').
37557        \param zstart Starting Z-coordinate of the instance image.
37558        \param zend Ending Z-coordinate of the instance image.
37559        \param zstride Stride along the Z-axis.
37560        \param zdilation Dilation along the Z-axis.
37561        \note
37562        - The correlation of the image instance \p *this by the kernel \p kernel is defined to be:
37563        res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j -
37564                     c_y),\alpha_z\;z + \beta_z\;(k - c_z))*kernel(i,j,k).
37565     **/
37566     template<typename t>
37567     CImg<T>& correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
37568                        const bool is_normalized=false, const unsigned int channel_mode=1,
37569                        const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U,
37570                        const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0,
37571                        const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U,
37572                        const float xstride=1, const float ystride=1, const float zstride=1,
37573                        const float xdilation=1, const float ydilation=1, const float zdilation=1) {
37574       if (is_empty() || !kernel) return *this;
37575       return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode,
37576                            xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
37577                            xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this);
37578     }
37579 
37580     template<typename t>
37581     CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
37582                                       const bool is_normalized=false, const unsigned int channel_mode=1,
37583                                       const unsigned int xcenter=~0U, const unsigned int ycenter=~0U,
37584                                       const unsigned int zcenter=~0U,
37585                                       const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0,
37586                                       const unsigned int xend=~0U, const unsigned int yend=~0U,
37587                                       const unsigned int zend=~0U,
37588                                       const float xstride=1, const float ystride=1, const float zstride=1,
37589                                       const float xdilation=1, const float ydilation=1, const float zdilation=1) const {
37590       return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
37591                         xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
37592                         xstride,ystride,zstride,xdilation,ydilation,zdilation,false);
37593     }
37594 
37595     //! Correlate image by a kernel \newinstance.
37596     template<typename t>
37597     CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const unsigned int boundary_conditions,
37598                                    const bool is_normalized, const unsigned int channel_mode,
37599                                    const unsigned int xcenter, const unsigned int ycenter, const unsigned int zcenter,
37600                                    const unsigned int xstart, const unsigned int ystart, const unsigned zstart,
37601                                    const unsigned int xend, const unsigned int yend, const unsigned int zend,
37602                                    const float xstride, const float ystride, const float zstride,
37603                                    const float xdilation, const float ydilation, const float zdilation,
37604                                    const bool is_convolve) const {
37605       if (is_empty() || !kernel) return *this;
37606       typedef _cimg_Ttfloat Ttfloat;
37607       CImg<Ttfloat> res;
37608       _cimg_abort_init_openmp;
37609       cimg_abort_init;
37610 
37611       if (xstart>xend || ystart>yend || zstart>zend)
37612         throw CImgArgumentException(_cimg_instance
37613                                     "%s(): Invalid xyz-start/end arguments (start = (%u,%u,%u), end = (%u,%u,%u)).",
37614                                     cimg_instance,
37615                                     is_convolve?"convolve":"correlate",
37616                                     xstart,ystart,zstart,xend,yend,zend);
37617       if (xstride<=0 || ystride<=0 || zstride<=0)
37618         throw CImgArgumentException(_cimg_instance
37619                                     "%s(): Invalid stride arguments (%g,%g,%g).",
37620                                     cimg_instance,
37621                                     is_convolve?"convolve":"correlate",
37622                                     xstride,ystride,zstride);
37623       const int
37624         _xstart = (int)std::min(xstart,_width - 1),
37625         _ystart = (int)std::min(ystart,_height - 1),
37626         _zstart = (int)std::min(zstart,_depth - 1),
37627         _xend = (int)std::min(xend,_width - 1),
37628         _yend = (int)std::min(yend,_height - 1),
37629         _zend = (int)std::min(zend,_depth - 1),
37630         nwidth = 1 + (int)std::floor((_xend - _xstart)/xstride),
37631         nheight = 1 + (int)std::floor((_yend - _ystart)/ystride),
37632         ndepth = 1 + (int)std::floor((_zend + _zstart)/zstride),
37633         _xstride = (int)cimg::round(xstride),
37634         _ystride = (int)cimg::round(ystride),
37635         _zstride = (int)cimg::round(zstride);
37636 
37637         const ulongT
37638           res_whd = (ulongT)nwidth*nheight*ndepth,
37639           res_size = res_whd*res._spectrum;
37640         const bool
37641           is_inner_parallel = res_whd>=(cimg_openmp_sizefactor)*32768,
37642           is_outer_parallel = res_size>=(cimg_openmp_sizefactor)*32768;
37643         cimg::unused(is_inner_parallel,is_outer_parallel);
37644 
37645       int
37646         _xcenter = xcenter==~0U?kernel.width()/2 - 1 + (kernel.width()%2):(int)std::min(xcenter,kernel._width - 1),
37647         _ycenter = ycenter==~0U?kernel.height()/2 - 1 + (kernel.height()%2):(int)std::min(ycenter,kernel._height - 1),
37648         _zcenter = zcenter==~0U?kernel.depth()/2 - 1 + (kernel.depth()%2):(int)std::min(zcenter,kernel._depth - 1),
37649         _xdilation = (int)cimg::round(xdilation),
37650         _ydilation = (int)cimg::round(ydilation),
37651         _zdilation = (int)cimg::round(zdilation);
37652 
37653       const bool is_int_stride_dilation =
37654         xstride==_xstride && ystride==_ystride && zstride==_zstride &&
37655         xdilation==_xdilation && ydilation==_ydilation && zdilation==_zdilation;
37656 
37657       CImg<t> _kernel;
37658       if (is_convolve) { // If convolution, go back to correlation
37659         _kernel = CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true).
37660           get_mirror('x').resize(kernel,-1);
37661         _xcenter = kernel.width() - 1 - _xcenter;
37662         _ycenter = kernel.height() - 1 - _ycenter;
37663         _zcenter = kernel.depth() - 1 - _zcenter;
37664       } else _kernel = kernel.get_shared();
37665 
37666       if (_kernel._width==_kernel._height && _kernel._width>1 && _kernel._height>1 &&
37667           ((_kernel._depth==1 && _kernel._width<=5) || (_kernel._depth==_kernel._width && _kernel._width<=3)) &&
37668           boundary_conditions<=1 && channel_mode &&
37669           _xcenter==_kernel.width()/2 - 1 + (_kernel.width()%2) &&
37670           _ycenter==_kernel.height()/2 - 1 + (_kernel.height()%2) &&
37671           _zcenter==_kernel.depth()/2 - 1 + (_kernel.depth()%2) &&
37672           is_int_stride_dilation && _xdilation>=0 && _ydilation>=0 && _zdilation>=0) {
37673 
37674         // Optimized versions for centered 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 kernels.
37675         const int dw = 1 - (_kernel.width()%2), dh = 1 - (_kernel.height()%2), dd = 1 - (_kernel.depth()%2);
37676         if (dw || dh || dd) // Force kernel size to be odd
37677           _kernel.get_resize(_kernel.width() + dw,_kernel.height() + dh,_kernel.depth() + dd,_kernel.spectrum(),
37678                              0,0,1,1,1).move_to(_kernel.assign());
37679 
37680         if (!boundary_conditions) { // Dirichlet -> Add a 1px zero border, then use _correlate() with Neumann
37681           const int
37682             dx = _kernel._width==1?0:1,
37683             dy = _kernel._height==1?0:1,
37684             dz = _kernel._depth==1?0:1;
37685           return get_crop(-dx,-dy,-dz,width() - 1 + dx,height() - 1 + dy,depth() - 1 + dz).
37686             _correlate(_kernel,true,is_normalized,channel_mode,_xcenter,_ycenter,_zcenter,
37687                        _xstart + dx,_ystart + dy,_zstart + dz,_xend + dx,_yend + dy,_zend + dz,
37688                        xstride,ystride,zstride,xdilation,ydilation,zdilation,false);
37689 
37690         } else { // Neumann boundaries
37691           res.assign(nwidth,nheight,ndepth,std::max(_spectrum,_kernel._spectrum));
37692 
37693           switch (_kernel._depth) {
37694           case 3 : { // 3x3x3 centered kernel
37695             cimg_forC(res,c) {
37696               cimg_abort_test;
37697               const CImg<T> I = get_shared_channel(c%_spectrum);
37698               const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
37699               const int w1 = I.width() - 1, h1 = I.height() - 1, d1 = I.depth() - 1;
37700               CImg<Ttfloat> _res = res.get_shared_channel(c);
37701               if (is_normalized) {
37702                 const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
37703                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
37704                   cimg_forXYZ(res,X,Y,Z) {
37705                   const int
37706                     x = _xstart + _xstride*X, y = _ystart + _ystride*Y, z = _zstart + _zstride*Z,
37707                     px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation<w1?x + _xdilation:w1,
37708                     py = y - _ydilation>0?y - _ydilation:0, ny = y + _ydilation<h1?y + _ydilation:h1,
37709                     pz = z - _zdilation>0?z - _zdilation:0, nz = z + _zdilation<d1?z + _zdilation:d1;
37710                   const Ttfloat N = M2*(cimg::sqr(I(px,py,pz)) + cimg::sqr(I(x,py,pz)) + cimg::sqr(I(nx,py,pz)) +
37711                                         cimg::sqr(I(px,y,pz)) + cimg::sqr(I(x,y,pz)) + cimg::sqr(I(nx,y,pz)) +
37712                                         cimg::sqr(I(px,ny,pz)) + cimg::sqr(I(x,ny,pz)) + cimg::sqr(I(nx,ny,pz)) +
37713                                         cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
37714                                         cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
37715                                         cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)) +
37716                                         cimg::sqr(I(px,py,nz)) + cimg::sqr(I(x,py,nz)) + cimg::sqr(I(nx,py,nz)) +
37717                                         cimg::sqr(I(px,y,nz)) + cimg::sqr(I(x,y,nz)) + cimg::sqr(I(nx,y,nz)) +
37718                                         cimg::sqr(I(px,ny,nz)) + cimg::sqr(I(x,ny,nz)) + cimg::sqr(I(nx,ny,nz)));
37719                   _res(X,Y,Z) = (Ttfloat)(N?(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
37720                                              K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
37721                                              K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
37722                                              K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
37723                                              K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
37724                                              K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
37725                                              K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
37726                                              K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
37727                                              K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz))/std::sqrt(N):0);
37728                 }
37729               } else {
37730                 cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
37731                   cimg_forXYZ(res,X,Y,Z) {
37732                   const int
37733                     x = _xstart + _xstride*X, y = _ystart + _ystride*Y, z = _zstart + _zstride*Z,
37734                     px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation<w1?x + _xdilation:w1,
37735                     py = y - _ydilation>0?y - _ydilation:0, ny = y + _ydilation<h1?y + _ydilation:h1,
37736                     pz = z - _zdilation>0?z - _zdilation:0, nz = z + _zdilation<d1?z + _zdilation:d1;
37737                   _res(X,Y,Z) = (Ttfloat)(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
37738                                           K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
37739                                           K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
37740                                           K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
37741                                           K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
37742                                           K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
37743                                           K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
37744                                           K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
37745                                           K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz));
37746                 }
37747               }
37748             }
37749           } break;
37750 
37751           default :
37752           case 1 :
37753             switch (_kernel._width) {
37754             case 5 : { // 5x5 centered kernel
37755               cimg_forC(res,c) {
37756                 cimg_abort_test;
37757                 const CImg<T> I = get_shared_channel(c%_spectrum);
37758                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
37759                 const int w1 = I.width() - 1, h1 = I.height() - 1;
37760                 CImg<Ttfloat> _res = res.get_shared_channel(c);
37761                 if (is_normalized) {
37762                   const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
37763                   cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
37764                   cimg_forXYZ(res,X,Y,z) {
37765                     const int
37766                       x = _xstart + _xstride*X, y = _ystart + _ystride*Y,
37767                       px = x - _xdilation>0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0,
37768                       nx = x + _xdilation<w1?x + _xdilation:w1, ax = nx + _xdilation<w1?nx + _xdilation:w1,
37769                       py = y - _ydilation>0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0,
37770                       ny = y + _ydilation<h1?y + _ydilation:h1, ay = ny + _ydilation<h1?ny + _ydilation:h1;
37771                     const Ttfloat N = M2*(cimg::sqr(I(bx,by,z)) + cimg::sqr(I(px,by,z)) + cimg::sqr(I(x,by,z)) +
37772                                           cimg::sqr(I(nx,by,z)) + cimg::sqr(I(ax,by,z)) +
37773                                           cimg::sqr(I(bx,py,z)) + cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) +
37774                                           cimg::sqr(I(nx,py,z)) + cimg::sqr(I(ax,py,z)) +
37775                                           cimg::sqr(I(bx,y,z)) + cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) +
37776                                           cimg::sqr(I(nx,y,z)) + cimg::sqr(I(ax,y,z)) +
37777                                           cimg::sqr(I(bx,ny,z)) + cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) +
37778                                           cimg::sqr(I(nx,ny,z)) + cimg::sqr(I(ax,ny,z)) +
37779                                           cimg::sqr(I(bx,ay,z)) + cimg::sqr(I(px,ay,z)) + cimg::sqr(I(x,ay,z)) +
37780                                           cimg::sqr(I(nx,ay,z)) + cimg::sqr(I(ax,ay,z)));
37781                     _res(X,Y,z) = (Ttfloat)(N?(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
37782                                                K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
37783                                                K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
37784                                                K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
37785                                                K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
37786                                                K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
37787                                                K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
37788                                                K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
37789                                                K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
37790                                                K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z))/std::sqrt(N):0);
37791                   }
37792                 } else {
37793                   cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
37794                     cimg_forXYZ(res,X,Y,z) {
37795                     const int
37796                       x = _xstart + _xstride*X, y = _ystart + _ystride*Y,
37797                       px = x - _xdilation>0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0,
37798                       nx = x + _xdilation<w1?x + _xdilation:w1, ax = nx + _xdilation<w1?nx + _xdilation:w1,
37799                       py = y - _ydilation>0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0,
37800                       ny = y + _ydilation<h1?y + _ydilation:h1, ay = ny + _ydilation<h1?ny + _ydilation:h1;
37801                     _res(X,Y,z) = (Ttfloat)(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
37802                                             K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
37803                                             K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
37804                                             K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
37805                                             K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
37806                                             K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
37807                                             K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
37808                                             K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
37809                                             K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
37810                                             K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z));
37811                   }
37812                 }
37813               }
37814             } break;
37815 
37816             case 3 : { // 3x3 centered kernel
37817               cimg_forC(res,c) {
37818                 cimg_abort_test;
37819                 const CImg<T> I = get_shared_channel(c%_spectrum);
37820                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
37821                 CImg<Ttfloat> _res = res.get_shared_channel(c);
37822                 const int w1 = I.width() - 1, h1 = I.height() - 1;
37823                 if (is_normalized) {
37824                   const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
37825                   cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384))
37826                   cimg_forXYZ(res,X,Y,z) {
37827                     const int
37828                       x = _xstart + _xstride*X, y = _ystart + _ystride*Y,
37829                       px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation<w1?x + _xdilation:w1,
37830                       py = y - _ydilation>0?y - _ydilation:0, ny = y + _ydilation<h1?y + _ydilation:h1;
37831                     const Ttfloat N = M2*(cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
37832                                           cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
37833                                           cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)));
37834                     _res(X,Y,z) = (Ttfloat)(N?(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
37835                                                K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
37836                                                K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z))/std::sqrt(N):0);
37837                   }
37838                 } else {
37839                   cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_res.size(),16384))
37840                   cimg_forXYZ(res,X,Y,z) {
37841                     const int
37842                       x = _xstart + _xstride*X, y = _ystart + _ystride*Y,
37843                       px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation<w1?x + _xdilation:w1,
37844                       py = y - _ydilation>0?y - _ydilation:0, ny = y + _ydilation<h1?y + _ydilation:h1;
37845                     _res(X,Y,z) = (Ttfloat)(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
37846                                             K[3]*I(px,y,z)  + K[4]*I(x,y,z)  + K[5]*I(nx,y,z) +
37847                                             K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z));
37848                   }
37849                 }
37850               }
37851             } break;
37852             }
37853           }
37854         }
37855       } else if (_kernel._width==1 && _kernel._height==1 && _kernel._depth==1 &&
37856                  xstride==1 && ystride==1 && zstride==1) {
37857 
37858         // Special optimization for 1x1 kernel.
37859         res = get_crop(_xstart,_ystart,_zstart,_xend,_yend,_zend);
37860         switch (channel_mode) {
37861         case 0 : { // Sum input channels
37862           CImg<T> res0 = res.get_shared_channel(0);
37863           for (int c = 1; c<res.spectrum(); ++c) res0+=res.get_shared_channel(c);
37864           res.channel(0).resize(-100,-100,-100,_kernel._spectrum,0,2);
37865           cimg_forC(res,c) res.get_shared_channel(c)*=_kernel[c];
37866         } break;
37867         case 1 : { // One-for-one
37868           res.resize(-100,-100,-100,std::max(res._spectrum,_kernel._spectrum),0,2);
37869           cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
37870           cimg_forC(res,c) res.get_shared_channel(c)*=_kernel[c%_kernel._spectrum];
37871         } break;
37872         default: { // Expand
37873           res.resize(-100,-100,-100,res._spectrum*_kernel._spectrum,0,2);
37874           cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
37875           cimg_forC(res,c) res.get_shared_channel(c)*=_kernel[c/res._spectrum];
37876         }
37877         }
37878       } else {
37879 
37880         // Generic version for other kernels and boundary conditions.
37881         res.assign(nwidth,nheight,ndepth,
37882                    channel_mode==0?_kernel._spectrum:
37883                    channel_mode==1?std::max(_spectrum,_kernel._spectrum):
37884                    _spectrum*_kernel._spectrum);
37885 
37886         if (!channel_mode) { // Channel mode: Sum inputs
37887           cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
37888           cimg_forC(_kernel,kc) _cimg_abort_try_openmp {
37889             cimg_abort_test;
37890             const CImg<t> K = _kernel.get_shared_channel(kc%_kernel._spectrum);
37891             int w2 = 0, h2 = 0, d2 = 0;
37892             Ttfloat M = 0, M2 = 0;
37893             if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; }
37894             if (boundary_conditions>=3) { w2 = 2*width(); h2 = 2*height(); d2 = 2*depth(); }
37895             res.fill(0);
37896             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
37897               cimg_forXYZC(res,x,y,z,c) {
37898               Ttfloat _val, val = 0, N = 0;
37899 
37900               if (is_int_stride_dilation)
37901                 cimg_forXYZ(_kernel,p,q,r) {
37902                   const int
37903                     ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter),
37904                     iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter),
37905                     iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter);
37906                   switch (boundary_conditions) {
37907                   case 0 : _val = atXYZ(ix,iy,iz,c,0); break; // Dirichlet
37908                   case 1 : _val = _atXYZ(ix,iy,iz,c); break; // Neumann
37909                   case 2 : _val = (*this)(cimg::mod(ix,width()),cimg::mod(iy,height()), // Periodic
37910                                           cimg::mod(iz,depth()),c); break;
37911                   default : { // Mirror
37912                     const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2);
37913                     _val = (*this)(mx<width()?mx:w2 - mx - 1,
37914                                    my<height()?my:h2 - my - 1,
37915                                    mz<depth()?mz:d2 - mz - 1);
37916                   }
37917                   }
37918                   val+=_val*K(p,q,r);
37919                   if (is_normalized) N+=_val*_val;
37920                 }
37921               else
37922                 cimg_forXYZ(_kernel,p,q,r) {
37923                   const float
37924                     ix = xstart + xstride*x + xdilation*(p - _xcenter),
37925                     iy = ystart + ystride*y + ydilation*(q - _ycenter),
37926                     iz = zstart + zstride*z + zdilation*(r - _zcenter);
37927                   switch (boundary_conditions) {
37928                   case 0 : _val = linear_atXYZ(ix,iy,iz,c,0); break; // Dirichlet
37929                   case 1 : _val = _linear_atXYZ(ix,iy,iz,c); break; // Neumann
37930                   case 2 : _val = _linear_atXYZ_p(ix,iy,iz,c); break; // Periodic
37931                   default : { // Mirror
37932                     const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2);
37933                     _val = linear_atXYZ(mx<width()?mx:w2 - mx - 1,
37934                                         my<height()?my:h2 - my - 1,
37935                                         mz<depth()?mz:d2 - mz - 1,c);
37936                   }
37937                   }
37938                   val+=_val*K(p,q,r);
37939                   if (is_normalized) N+=_val*_val;
37940                 }
37941               N*=M2;
37942               res(x,y,z,c) += is_normalized?(Ttfloat)(N?val/std::sqrt(N):0):val;
37943             }
37944           } _cimg_abort_catch_openmp
37945           cimg_abort_test;
37946 
37947         } else { // Channel mode: one-for-one & expand
37948           cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
37949             cimg_forC(res,c) _cimg_abort_try_openmp {
37950             cimg_abort_test;
37951             const CImg<T> I = get_shared_channel(c%_spectrum);
37952             const CImg<t> K = _kernel.get_shared_channel(channel_mode==1?c%_kernel._spectrum:c/_spectrum);
37953             int w2 = 0, h2 = 0, d2 = 0;
37954             Ttfloat M = 0, M2 = 0;
37955             if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; }
37956             if (boundary_conditions>=3) { w2 = 2*I.width(); h2 = 2*I.height(); d2 = 2*I.depth(); }
37957             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
37958               cimg_forXYZ(res,x,y,z) {
37959               Ttfloat _val, val = 0, N = 0;
37960 
37961               if (is_int_stride_dilation)
37962                 cimg_forXYZ(_kernel,p,q,r) {
37963                   const int
37964                     ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter),
37965                     iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter),
37966                     iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter);
37967                   switch (boundary_conditions) {
37968                   case 0 : _val = I.atXYZ(ix,iy,iz,0,0); break; // Dirichlet
37969                   case 1 : _val = I._atXYZ(ix,iy,iz); break; // Neumann
37970                   case 2 : _val = I(cimg::mod(ix,I.width()),cimg::mod(iy,I.height()), // Periodic
37971                                     cimg::mod(iz,I.depth())); break;
37972                   default : { // Mirror
37973                     const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2);
37974                     _val = I(mx<I.width()?mx:w2 - mx - 1,
37975                              my<I.height()?my:h2 - my - 1,
37976                              mz<I.depth()?mz:d2 - mz - 1);
37977                   }
37978                   }
37979                   val+=_val*K(p,q,r);
37980                   if (is_normalized) N+=_val*_val;
37981                 }
37982               else
37983                 cimg_forXYZ(_kernel,p,q,r) {
37984                   const float
37985                     ix = xstart + xstride*x + xdilation*(p - _xcenter),
37986                     iy = ystart + ystride*y + ydilation*(q - _ycenter),
37987                     iz = zstart + zstride*z + zdilation*(r - _zcenter);
37988                   switch (boundary_conditions) {
37989                   case 0 : _val = I.linear_atXYZ(ix,iy,iz,0,0); break; // Dirichlet
37990                   case 1 : _val = I._linear_atXYZ(ix,iy,iz); break; // Neumann
37991                   case 2 : _val = I._linear_atXYZ_p(ix,iy,iz); break; // Periodic
37992                   default : { // Mirror
37993                     const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2);
37994                     _val = I.linear_atXYZ(mx<I.width()?mx:w2 - mx - 1,
37995                                           my<I.height()?my:h2 - my - 1,
37996                                           mz<I.depth()?mz:d2 - mz - 1);
37997                   }
37998                   }
37999                   val+=_val*K(p,q,r);
38000                   if (is_normalized) N+=_val*_val;
38001                 }
38002               N*=M2;
38003               res(x,y,z,c) = is_normalized?(Ttfloat)(N?val/std::sqrt(N):0):val;
38004             }
38005           } _cimg_abort_catch_openmp
38006           cimg_abort_test;
38007         }
38008       }
38009       return res;
38010     }
38011 
38012     //! Convolve image by a kernel.
38013     /**
38014        \param kernel = the correlation kernel.
38015        \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
38016        \param is_normalized = enable local normalization.
38017        \param channel mode Channel processing mode. Can be { 0=sum inputs | 1=one-for-one | 2=expand }
38018        \param xcenter X-coordinate of the kernel center (~0U means 'centered').
38019        \param xstart Starting X-coordinate of the instance image.
38020        \param xend Ending X-coordinate of the instance image.
38021        \param xstride Stride along the X-axis.
38022        \param xdilation Dilation along the X-axis.
38023        \param ycenter Y-coordinate of the kernel center (~0U means 'centered').
38024        \param ystart Starting Y-coordinate of the instance image.
38025        \param yend Ending Y-coordinate of the instance image.
38026        \param ystride Stride along the Y-axis.
38027        \param ydilation Dilation along the Y-axis.
38028        \param zcenter Z-coordinate of the kernel center (~0U means 'centered').
38029        \param zstart Starting Z-coordinate of the instance image.
38030        \param zend Ending Z-coordinate of the instance image.
38031        \param zstride Stride along the Z-axis.
38032        \param zdilation Dilation along the Z-axis.
38033        \note
38034        - The convolution of the image instance \p *this by the kernel \p kernel is defined to be:
38035        res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x - \beta_x\;(i - c_x),\alpha_y\;y
38036                     - \beta_y\;(j - c_y),\alpha_z\;z - \beta_z\;(k - c_z))*kernel(i,j,k).
38037     **/
38038     template<typename t>
38039     CImg<T>& convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
38040                       const bool is_normalized=false, const unsigned int channel_mode=1,
38041                       const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U,
38042                       const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0,
38043                       const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U,
38044                       const float xstride=1, const float ystride=1, const float zstride=1,
38045                       const float xdilation=1, const float ydilation=1, const float zdilation=1) {
38046       if (is_empty() || !kernel) return *this;
38047       return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode,
38048                           xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38049                           xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this);
38050     }
38051 
38052     //! Convolve image by a kernel \newinstance.
38053     template<typename t>
38054     CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
38055                                      const bool is_normalized=false, const unsigned int channel_mode=1,
38056                                       const unsigned int xcenter=~0U, const unsigned int ycenter=~0U,
38057                                       const unsigned int zcenter=~0U,
38058                                       const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0,
38059                                       const unsigned int xend=~0U, const unsigned int yend=~0U,
38060                                       const unsigned int zend=~0U,
38061                                       const float xstride=1, const float ystride=1, const float zstride=1,
38062                                       const float xdilation=1, const float ydilation=1, const float zdilation=1) const {
38063       return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
38064                         xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
38065                         xstride,ystride,zstride,xdilation,ydilation,zdilation,true);
38066     }
38067 
38068     //! Cumulate image values, optionally along specified axis.
38069     /**
38070        \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account.
38071     **/
38072     CImg<T>& cumulate(const char axis=0) {
38073       switch (cimg::lowercase(axis)) {
38074       case 'x' :
38075         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
38076                                                                    _height*_depth*_spectrum>=16))
38077         cimg_forYZC(*this,y,z,c) {
38078           T *ptrd = data(0,y,z,c);
38079           Tlong cumul = (Tlong)0;
38080           cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; }
38081         }
38082         break;
38083       case 'y' : {
38084         const ulongT w = (ulongT)_width;
38085         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 &&
38086                                                                    _width*_depth*_spectrum>=16))
38087         cimg_forXZC(*this,x,z,c) {
38088           T *ptrd = data(x,0,z,c);
38089           Tlong cumul = (Tlong)0;
38090           cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; }
38091         }
38092       } break;
38093       case 'z' : {
38094         const ulongT wh = (ulongT)_width*_height;
38095         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 &&
38096                                                                    _width*_depth*_spectrum>=16))
38097         cimg_forXYC(*this,x,y,c) {
38098           T *ptrd = data(x,y,0,c);
38099           Tlong cumul = (Tlong)0;
38100           cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; }
38101         }
38102       } break;
38103       case 'c' : {
38104         const ulongT whd = (ulongT)_width*_height*_depth;
38105         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
38106                            cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && _width*_height*_depth>=16))
38107         cimg_forXYZ(*this,x,y,z) {
38108           T *ptrd = data(x,y,z,0);
38109           Tlong cumul = (Tlong)0;
38110           cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; }
38111         }
38112       } break;
38113       default : { // Global cumulation
38114         Tlong cumul = (Tlong)0;
38115         cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; }
38116       }
38117       }
38118       return *this;
38119     }
38120 
38121     //! Cumulate image values, optionally along specified axis \newinstance.
38122     CImg<Tlong> get_cumulate(const char axis=0) const {
38123       return CImg<Tlong>(*this,false).cumulate(axis);
38124     }
38125 
38126     //! Cumulate image values, along specified axes.
38127     /**
38128        \param axes Cumulation axes, as a C-string.
38129        \note \c axes may contains multiple characters, e.g. \c "xyz"
38130     **/
38131     CImg<T>& cumulate(const char *const axes) {
38132       for (const char *s = axes; *s; ++s) cumulate(*s);
38133       return *this;
38134     }
38135 
38136     //! Cumulate image values, along specified axes \newinstance.
38137     CImg<Tlong> get_cumulate(const char *const axes) const {
38138       return CImg<Tlong>(*this,false).cumulate(axes);
38139     }
38140 
38141     //! Erode image by a structuring element.
38142     /**
38143        \param kernel Structuring element.
38144        \param boundary_conditions Boundary conditions.
38145        \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
38146     **/
38147     template<typename t>
38148     CImg<T>& erode(const CImg<t>& kernel, const bool boundary_conditions=true,
38149                    const bool is_real=false) {
38150       if (is_empty() || !kernel) return *this;
38151       return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
38152     }
38153 
38154     //! Erode image by a structuring element \newinstance.
38155     template<typename t>
38156     CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const bool boundary_conditions=true,
38157                              const bool is_real=false) const {
38158       if (is_empty() || !kernel) return *this;
38159       if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0);
38160       typedef _cimg_Tt Tt;
38161       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
38162       const int
38163         mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
38164         mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1,
38165         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
38166       const bool
38167         is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
38168         is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
38169       cimg::unused(is_inner_parallel,is_outer_parallel);
38170       _cimg_abort_init_openmp;
38171       cimg_abort_init;
38172       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
38173       cimg_forC(res,c) _cimg_abort_try_openmp {
38174         cimg_abort_test;
38175         const CImg<T> img = get_shared_channel(c%_spectrum);
38176         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
38177         if (is_real) { // Real erosion
38178           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38179           for (int z = mz1; z<mze; ++z)
38180             for (int y = my1; y<mye; ++y)
38181               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
38182                 cimg_abort_test2;
38183                 Tt min_val = cimg::type<Tt>::max();
38184                 for (int zm = -mz1; zm<=mz2; ++zm)
38185                   for (int ym = -my1; ym<=my2; ++ym)
38186                     for (int xm = -mx1; xm<=mx2; ++xm) {
38187                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
38188                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval);
38189                       if (cval<min_val) min_val = cval;
38190                     }
38191                 res(x,y,z,c) = min_val;
38192               } _cimg_abort_catch_openmp2
38193           if (boundary_conditions)
38194             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38195             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38196               cimg_abort_test2;
38197               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38198                 Tt min_val = cimg::type<Tt>::max();
38199                 for (int zm = -mz1; zm<=mz2; ++zm)
38200                   for (int ym = -my1; ym<=my2; ++ym)
38201                     for (int xm = -mx1; xm<=mx2; ++xm) {
38202                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
38203                       const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval);
38204                       if (cval<min_val) min_val = cval;
38205                     }
38206                 res(x,y,z,c) = min_val;
38207               }
38208             } _cimg_abort_catch_openmp2
38209           else
38210             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38211             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38212               cimg_abort_test2;
38213               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38214                 Tt min_val = cimg::type<Tt>::max();
38215                 for (int zm = -mz1; zm<=mz2; ++zm)
38216                   for (int ym = -my1; ym<=my2; ++ym)
38217                     for (int xm = -mx1; xm<=mx2; ++xm) {
38218                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
38219                       const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval);
38220                       if (cval<min_val) min_val = cval;
38221                     }
38222                 res(x,y,z,c) = min_val;
38223               }
38224             } _cimg_abort_catch_openmp2
38225 
38226         } else { // Binary erosion
38227           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38228           for (int z = mz1; z<mze; ++z)
38229             for (int y = my1; y<mye; ++y)
38230               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
38231                 cimg_abort_test2;
38232                 Tt min_val = cimg::type<Tt>::max();
38233                 for (int zm = -mz1; zm<=mz2; ++zm)
38234                   for (int ym = -my1; ym<=my2; ++ym)
38235                     for (int xm = -mx1; xm<=mx2; ++xm)
38236                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
38237                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
38238                         if (cval<min_val) min_val = cval;
38239                       }
38240                 res(x,y,z,c) = min_val;
38241               } _cimg_abort_catch_openmp2
38242           if (boundary_conditions)
38243             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38244             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38245               cimg_abort_test2;
38246               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38247                 Tt min_val = cimg::type<Tt>::max();
38248                 for (int zm = -mz1; zm<=mz2; ++zm)
38249                   for (int ym = -my1; ym<=my2; ++ym)
38250                     for (int xm = -mx1; xm<=mx2; ++xm)
38251                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
38252                         const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
38253                         if (cval<min_val) min_val = cval;
38254                       }
38255                 res(x,y,z,c) = min_val;
38256               }
38257             } _cimg_abort_catch_openmp2
38258           else
38259             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38260             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38261               cimg_abort_test2;
38262               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38263                 Tt min_val = cimg::type<Tt>::max();
38264                 for (int zm = -mz1; zm<=mz2; ++zm)
38265                   for (int ym = -my1; ym<=my2; ++ym)
38266                     for (int xm = -mx1; xm<=mx2; ++xm)
38267                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
38268                         const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
38269                         if (cval<min_val) min_val = cval;
38270                       }
38271                 res(x,y,z,c) = min_val;
38272               }
38273             } _cimg_abort_catch_openmp2
38274         }
38275       } _cimg_abort_catch_openmp
38276       cimg_abort_test;
38277       return res;
38278     }
38279 
38280     //! Erode image by a rectangular structuring element of specified size.
38281     /**
38282        \param sx Width of the structuring element.
38283        \param sy Height of the structuring element.
38284        \param sz Depth of the structuring element.
38285     **/
38286     CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
38287       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
38288       if (sx>1 && _width>1) { // Along X-axis
38289         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;
38290         CImg<T> buf(L);
38291         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38292         cimg_forYZC(*this,y,z,c) {
38293           T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
38294           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38295           T cur = *ptrs; ptrs+=off; bool is_first = true;
38296           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38297             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
38298           *(ptrd++) = cur;
38299           if (ptrs>=ptrse) {
38300             T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38301           } else {
38302             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38303               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38304               *(ptrd++) = cur;
38305             }
38306             for (int p = L - s - 1; p>0; --p) {
38307               const T val = *ptrs; ptrs+=off;
38308               if (is_first) {
38309                 const T *nptrs = ptrs - off; cur = val;
38310                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
38311                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
38312               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38313               *(ptrd++) = cur;
38314             }
38315             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38316             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38317               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
38318             }
38319             *(ptrd--) = cur;
38320             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38321               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
38322             }
38323             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38324           }
38325         }
38326       }
38327 
38328       if (sy>1 && _height>1) { // Along Y-axis
38329         const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
38330           s2 = _s2>L?L:_s2;
38331         CImg<T> buf(L);
38332         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38333         cimg_forXZC(*this,x,z,c) {
38334           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
38335           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38336           T cur = *ptrs; ptrs+=off; bool is_first = true;
38337           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38338             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38339           }
38340           *(ptrd++) = cur;
38341           if (ptrs>=ptrse) {
38342             T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38343           } else {
38344             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38345               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38346               *(ptrd++) = cur;
38347             }
38348             for (int p = L - s - 1; p>0; --p) {
38349               const T val = *ptrs; ptrs+=off;
38350               if (is_first) {
38351                 const T *nptrs = ptrs - off; cur = val;
38352                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
38353                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
38354               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38355               *(ptrd++) = cur;
38356             }
38357             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38358             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38359               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
38360             }
38361             *(ptrd--) = cur;
38362             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38363               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
38364             }
38365             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38366           }
38367         }
38368       }
38369 
38370       if (sz>1 && _depth>1) { // Along Z-axis
38371         const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
38372           s2 = _s2>L?L:_s2;
38373         CImg<T> buf(L);
38374         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38375         cimg_forXYC(*this,x,y,c) {
38376           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
38377           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38378           T cur = *ptrs; ptrs+=off; bool is_first = true;
38379           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38380             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38381           }
38382           *(ptrd++) = cur;
38383           if (ptrs>=ptrse) {
38384             T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38385           } else {
38386             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38387               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
38388               *(ptrd++) = cur;
38389             }
38390             for (int p = L - s - 1; p>0; --p) {
38391               const T val = *ptrs; ptrs+=off;
38392               if (is_first) {
38393                 const T *nptrs = ptrs - off; cur = val;
38394                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
38395                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
38396               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38397               *(ptrd++) = cur;
38398             }
38399             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38400             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38401               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
38402             }
38403             *(ptrd--) = cur;
38404             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38405               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
38406             }
38407             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38408           }
38409         }
38410       }
38411       return *this;
38412     }
38413 
38414     //! Erode image by a rectangular structuring element of specified size \newinstance.
38415     CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
38416       return (+*this).erode(sx,sy,sz);
38417     }
38418 
38419     //! Erode the image by a square structuring element of specified size.
38420     /**
38421        \param s Size of the structuring element.
38422     **/
38423     CImg<T>& erode(const unsigned int s) {
38424       return erode(s,s,s);
38425     }
38426 
38427     //! Erode the image by a square structuring element of specified size \newinstance.
38428     CImg<T> get_erode(const unsigned int s) const {
38429       return (+*this).erode(s);
38430     }
38431 
38432     //! Dilate image by a structuring element.
38433     /**
38434        \param kernel Structuring element.
38435        \param boundary_conditions Boundary conditions.
38436        \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
38437     **/
38438     template<typename t>
38439     CImg<T>& dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
38440                     const bool is_real=false) {
38441       if (is_empty() || !kernel) return *this;
38442       return get_dilate(kernel,boundary_conditions,is_real).move_to(*this);
38443     }
38444 
38445     //! Dilate image by a structuring element \newinstance.
38446     template<typename t>
38447     CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
38448                               const bool is_real=false) const {
38449       if (is_empty() || !kernel || (!is_real && kernel==0)) return *this;
38450       typedef _cimg_Tt Tt;
38451       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
38452       const int
38453         mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2,
38454         mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1,
38455         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
38456       const bool
38457         is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
38458         is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
38459       cimg::unused(is_inner_parallel,is_outer_parallel);
38460       _cimg_abort_init_openmp;
38461       cimg_abort_init;
38462       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
38463       cimg_forC(res,c) _cimg_abort_try_openmp {
38464         cimg_abort_test;
38465         const CImg<T> img = get_shared_channel(c%_spectrum);
38466         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
38467         if (is_real) { // Real dilation
38468           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38469           for (int z = mz1; z<mze; ++z)
38470             for (int y = my1; y<mye; ++y)
38471               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
38472                 cimg_abort_test2;
38473                 Tt max_val = cimg::type<Tt>::min();
38474                 for (int zm = -mz1; zm<=mz2; ++zm)
38475                   for (int ym = -my1; ym<=my2; ++ym)
38476                     for (int xm = -mx1; xm<=mx2; ++xm) {
38477                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
38478                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval);
38479                       if (cval>max_val) max_val = cval;
38480                     }
38481                 res(x,y,z,c) = max_val;
38482               } _cimg_abort_catch_openmp2
38483           if (boundary_conditions)
38484             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38485             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38486               cimg_abort_test2;
38487               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38488                 Tt max_val = cimg::type<Tt>::min();
38489                 for (int zm = -mz1; zm<=mz2; ++zm)
38490                   for (int ym = -my1; ym<=my2; ++ym)
38491                     for (int xm = -mx1; xm<=mx2; ++xm) {
38492                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
38493                       const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval);
38494                       if (cval>max_val) max_val = cval;
38495                     }
38496                 res(x,y,z,c) = max_val;
38497               }
38498             } _cimg_abort_catch_openmp2
38499           else
38500             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38501             cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 {
38502               cimg_abort_test2;
38503               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38504                 Tt max_val = cimg::type<Tt>::min();
38505                 for (int zm = -mz1; zm<=mz2; ++zm)
38506                   for (int ym = -my1; ym<=my2; ++ym)
38507                     for (int xm = -mx1; xm<=mx2; ++xm) {
38508                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
38509                       const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval);
38510                       if (cval>max_val) max_val = cval;
38511                     }
38512                 res(x,y,z,c) = max_val;
38513               }
38514             } _cimg_abort_catch_openmp2
38515         } else { // Binary dilation
38516           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
38517           for (int z = mz1; z<mze; ++z)
38518             for (int y = my1; y<mye; ++y)
38519               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
38520                 cimg_abort_test2;
38521                 Tt max_val = cimg::type<Tt>::min();
38522                 for (int zm = -mz1; zm<=mz2; ++zm)
38523                   for (int ym = -my1; ym<=my2; ++ym)
38524                     for (int xm = -mx1; xm<=mx2; ++xm)
38525                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
38526                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
38527                         if (cval>max_val) max_val = cval;
38528                       }
38529                 res(x,y,z,c) = max_val;
38530               } _cimg_abort_catch_openmp2
38531           if (boundary_conditions)
38532             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38533             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38534               cimg_abort_test2;
38535               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38536                 Tt max_val = cimg::type<Tt>::min();
38537                 for (int zm = -mz1; zm<=mz2; ++zm)
38538                   for (int ym = -my1; ym<=my2; ++ym)
38539                     for (int xm = -mx1; xm<=mx2; ++xm)
38540                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
38541                         const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
38542                         if (cval>max_val) max_val = cval;
38543                       }
38544                 res(x,y,z,c) = max_val;
38545               }
38546             } _cimg_abort_catch_openmp2
38547           else
38548             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
38549             cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
38550               cimg_abort_test2;
38551               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
38552                 Tt max_val = cimg::type<Tt>::min();
38553                 for (int zm = -mz1; zm<=mz2; ++zm)
38554                   for (int ym = -my1; ym<=my2; ++ym)
38555                     for (int xm = -mx1; xm<=mx2; ++xm)
38556                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
38557                         const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
38558                         if (cval>max_val) max_val = cval;
38559                       }
38560                 res(x,y,z,c) = max_val;
38561               }
38562             } _cimg_abort_catch_openmp2
38563         }
38564       } _cimg_abort_catch_openmp
38565       cimg_abort_test;
38566       return res;
38567     }
38568 
38569     //! Dilate image by a rectangular structuring element of specified size.
38570     /**
38571        \param sx Width of the structuring element.
38572        \param sy Height of the structuring element.
38573        \param sz Depth of the structuring element.
38574     **/
38575     CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
38576       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
38577       if (sx>1 && _width>1) { // Along X-axis
38578         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;
38579         CImg<T> buf(L);
38580         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38581         cimg_forYZC(*this,y,z,c) {
38582           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
38583           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38584           T cur = *ptrs; ptrs+=off; bool is_first = true;
38585           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38586             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
38587           }
38588           *(ptrd++) = cur;
38589           if (ptrs>=ptrse) {
38590             T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38591           } else {
38592             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38593               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
38594               *(ptrd++) = cur;
38595             }
38596             for (int p = L - s - 1; p>0; --p) {
38597               const T val = *ptrs; ptrs+=off;
38598               if (is_first) {
38599                 const T *nptrs = ptrs - off; cur = val;
38600                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
38601                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
38602               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38603               *(ptrd++) = cur;
38604             }
38605             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38606             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38607               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
38608             }
38609             *(ptrd--) = cur;
38610             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38611               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
38612             }
38613             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38614           }
38615         }
38616       }
38617 
38618       if (sy>1 && _height>1) { // Along Y-axis
38619         const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
38620           s2 = _s2>L?L:_s2;
38621         CImg<T> buf(L);
38622         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38623         cimg_forXZC(*this,x,z,c) {
38624           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
38625           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38626           T cur = *ptrs; ptrs+=off; bool is_first = true;
38627           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38628             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
38629           }
38630           *(ptrd++) = cur;
38631           if (ptrs>=ptrse) {
38632             T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38633           } else {
38634             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38635               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
38636               *(ptrd++) = cur;
38637             }
38638             for (int p = L - s - 1; p>0; --p) {
38639               const T val = *ptrs; ptrs+=off;
38640               if (is_first) {
38641                 const T *nptrs = ptrs - off; cur = val;
38642                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
38643                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
38644               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38645               *(ptrd++) = cur;
38646             }
38647             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38648             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38649               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
38650             }
38651             *(ptrd--) = cur;
38652             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38653               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
38654             }
38655             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38656           }
38657         }
38658       }
38659 
38660       if (sz>1 && _depth>1) { // Along Z-axis
38661         const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
38662           s2 = _s2>L?L:_s2;
38663         CImg<T> buf(L);
38664         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
38665         cimg_forXYC(*this,x,y,c) {
38666           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
38667           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
38668           T cur = *ptrs; ptrs+=off; bool is_first = true;
38669           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
38670             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
38671           }
38672           *(ptrd++) = cur;
38673           if (ptrs>=ptrse) {
38674             T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
38675           } else {
38676             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
38677               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
38678               *(ptrd++) = cur;
38679             }
38680             for (int p = L - s - 1; p>0; --p) {
38681               const T val = *ptrs; ptrs+=off;
38682               if (is_first) {
38683                 const T *nptrs = ptrs - off; cur = val;
38684                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
38685                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
38686               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
38687               *(ptrd++) = cur;
38688             }
38689             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
38690             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
38691               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
38692             }
38693             *(ptrd--) = cur;
38694             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
38695               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
38696             }
38697             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
38698           }
38699         }
38700       }
38701       return *this;
38702     }
38703 
38704     //! Dilate image by a rectangular structuring element of specified size \newinstance.
38705     CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
38706       return (+*this).dilate(sx,sy,sz);
38707     }
38708 
38709     //! Dilate image by a square structuring element of specified size.
38710     /**
38711        \param s Size of the structuring element.
38712     **/
38713     CImg<T>& dilate(const unsigned int s) {
38714       return dilate(s,s,s);
38715     }
38716 
38717     //! Dilate image by a square structuring element of specified size \newinstance.
38718     CImg<T> get_dilate(const unsigned int s) const {
38719       return (+*this).dilate(s);
38720     }
38721 
38722     //! Compute watershed transform.
38723     /**
38724        \param priority Priority map.
38725        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
38726        in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
38727        \note Non-zero values of the instance instance are propagated to zero-valued ones according to
38728        specified the priority map.
38729     **/
38730     template<typename t>
38731     CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) {
38732 #define _cimg_watershed_init(cond,X,Y,Z) \
38733       if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds)
38734 
38735 #define _cimg_watershed_propagate(cond,X,Y,Z) \
38736       if (cond) { \
38737         if ((*this)(X,Y,Z)) { \
38738           ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \
38739           d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \
38740           if (d<dmin) { dmin = d; nmin = ns; nlabel = (*this)(xs,ys,zs); } \
38741         } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \
38742       }
38743 
38744       if (is_empty()) return *this;
38745       if (!is_sameXYZ(priority))
38746         throw CImgArgumentException(_cimg_instance
38747                                     "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
38748                                     "have different dimensions.",
38749                                     cimg_instance,
38750                                     priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
38751       if (_spectrum!=1) {
38752         cimg_forC(*this,c)
38753           get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum));
38754         return *this;
38755       }
38756 
38757       CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3);
38758       CImg<typename cimg::superset2<T,t,int>::type> Q;
38759       unsigned int sizeQ = 0;
38760       int px, nx, py, ny, pz, nz;
38761       bool is_px, is_nx, is_py, is_ny, is_pz, is_nz;
38762       const bool is_3d = _depth>1;
38763 
38764       // Find seed points and insert them in priority queue.
38765       unsigned int nb_seeds = 0;
38766       const T *ptrs = _data;
38767       cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version
38768         if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0);
38769         seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z;
38770         px = x - 1; nx = x + 1;
38771         py = y - 1; ny = y + 1;
38772         pz = z - 1; nz = z + 1;
38773         is_px = px>=0; is_nx = nx<width();
38774         is_py = py>=0; is_ny = ny<height();
38775         is_pz = pz>=0; is_nz = nz<depth();
38776         _cimg_watershed_init(is_px,px,y,z);
38777         _cimg_watershed_init(is_nx,nx,y,z);
38778         _cimg_watershed_init(is_py,x,py,z);
38779         _cimg_watershed_init(is_ny,x,ny,z);
38780         if (is_3d) {
38781           _cimg_watershed_init(is_pz,x,y,pz);
38782           _cimg_watershed_init(is_nz,x,y,nz);
38783         }
38784         if (is_high_connectivity) {
38785           _cimg_watershed_init(is_px && is_py,px,py,z);
38786           _cimg_watershed_init(is_nx && is_py,nx,py,z);
38787           _cimg_watershed_init(is_px && is_ny,px,ny,z);
38788           _cimg_watershed_init(is_nx && is_ny,nx,ny,z);
38789           if (is_3d) {
38790             _cimg_watershed_init(is_px && is_pz,px,y,pz);
38791             _cimg_watershed_init(is_nx && is_pz,nx,y,pz);
38792             _cimg_watershed_init(is_px && is_nz,px,y,nz);
38793             _cimg_watershed_init(is_nx && is_nz,nx,y,nz);
38794             _cimg_watershed_init(is_py && is_pz,x,py,pz);
38795             _cimg_watershed_init(is_ny && is_pz,x,ny,pz);
38796             _cimg_watershed_init(is_py && is_nz,x,py,nz);
38797             _cimg_watershed_init(is_ny && is_nz,x,ny,nz);
38798             _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz);
38799             _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz);
38800             _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz);
38801             _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz);
38802             _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz);
38803             _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz);
38804             _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz);
38805             _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz);
38806           }
38807         }
38808         labels(x,y,z) = nb_seeds;
38809       }
38810 
38811       // Start watershed computation.
38812       while (sizeQ) {
38813 
38814         // Get and remove point with maximal priority from the queue.
38815         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
38816         const unsigned int n = labels(x,y,z);
38817         px = x - 1; nx = x + 1;
38818         py = y - 1; ny = y + 1;
38819         pz = z - 1; nz = z + 1;
38820         is_px = px>=0; is_nx = nx<width();
38821         is_py = py>=0; is_ny = ny<height();
38822         is_pz = pz>=0; is_nz = nz<depth();
38823 
38824         // Check labels of the neighbors.
38825         Q._priority_queue_remove(sizeQ);
38826 
38827         unsigned int xs, ys, zs, ns, nmin = 0;
38828         float d, dmin = cimg::type<float>::inf();
38829         T nlabel = (T)0;
38830         _cimg_watershed_propagate(is_px,px,y,z);
38831         _cimg_watershed_propagate(is_nx,nx,y,z);
38832         _cimg_watershed_propagate(is_py,x,py,z);
38833         _cimg_watershed_propagate(is_ny,x,ny,z);
38834         if (is_3d) {
38835           _cimg_watershed_propagate(is_pz,x,y,pz);
38836           _cimg_watershed_propagate(is_nz,x,y,nz);
38837         }
38838         if (is_high_connectivity) {
38839           _cimg_watershed_propagate(is_px && is_py,px,py,z);
38840           _cimg_watershed_propagate(is_nx && is_py,nx,py,z);
38841           _cimg_watershed_propagate(is_px && is_ny,px,ny,z);
38842           _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z);
38843           if (is_3d) {
38844             _cimg_watershed_propagate(is_px && is_pz,px,y,pz);
38845             _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz);
38846             _cimg_watershed_propagate(is_px && is_nz,px,y,nz);
38847             _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz);
38848             _cimg_watershed_propagate(is_py && is_pz,x,py,pz);
38849             _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz);
38850             _cimg_watershed_propagate(is_py && is_nz,x,py,nz);
38851             _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz);
38852             _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz);
38853             _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz);
38854             _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz);
38855             _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz);
38856             _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz);
38857             _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz);
38858             _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz);
38859             _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz);
38860           }
38861         }
38862         (*this)(x,y,z) = nlabel;
38863         labels(x,y,z) = ++nmin;
38864       }
38865       return *this;
38866     }
38867 
38868     //! Compute watershed transform \newinstance.
38869     template<typename t>
38870     CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const {
38871       return (+*this).watershed(priority,is_high_connectivity);
38872     }
38873 
38874     // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
38875     template<typename tq, typename tv>
38876     bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value,
38877                                 const unsigned int x, const unsigned int y, const unsigned int z,
38878                                 const unsigned int n=1) {
38879       if (is_queued(x,y,z)) return false;
38880       is_queued(x,y,z) = (tq)n;
38881       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
38882       (*this)(siz - 1,0) = (T)value;
38883       (*this)(siz - 1,1) = (T)x;
38884       (*this)(siz - 1,2) = (T)y;
38885       (*this)(siz - 1,3) = (T)z;
38886       for (unsigned int pos = siz - 1, par = 0; pos && value>(tv)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
38887         cimg::swap((*this)(pos,0),(*this)(par,0));
38888         cimg::swap((*this)(pos,1),(*this)(par,1));
38889         cimg::swap((*this)(pos,2),(*this)(par,2));
38890         cimg::swap((*this)(pos,3),(*this)(par,3));
38891       }
38892       return true;
38893     }
38894 
38895     CImg<T>& _priority_queue_remove(unsigned int& siz) {
38896       (*this)(0,0) = (*this)(--siz,0);
38897       (*this)(0,1) = (*this)(siz,1);
38898       (*this)(0,2) = (*this)(siz,2);
38899       (*this)(0,3) = (*this)(siz,3);
38900       const float value = (*this)(0,0);
38901       unsigned int pos = 0, swap = 0;
38902       do {
38903         const unsigned int left = 2*pos + 1, right = left + 1;
38904         if (right<siz && value<(*this)(right,0)) swap = (*this)(left,0)>(*this)(right,0)?left:right;
38905         else if (left<siz && value<(*this)(left,0)) swap = left;
38906         else break;
38907         cimg::swap((*this)(pos,0),(*this)(swap,0));
38908         cimg::swap((*this)(pos,1),(*this)(swap,1));
38909         cimg::swap((*this)(pos,2),(*this)(swap,2));
38910         cimg::swap((*this)(pos,3),(*this)(swap,3));
38911         pos = swap;
38912       } while (true);
38913       return *this;
38914     }
38915 
38916     //! Apply recursive Deriche filter.
38917     /**
38918        \param sigma Standard deviation of the filter.
38919        \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
38920        \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
38921        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
38922     **/
38923     CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x',
38924                      const bool boundary_conditions=true) {
38925 #define _cimg_deriche_apply \
38926   CImg<doubleT> Y(N); \
38927   double *ptrY = Y._data, yb = 0, yp = 0; \
38928   T xp = (T)0; \
38929   if (boundary_conditions) { xp = *ptrX; yb = yp = (double)(coefp*xp); } \
38930   for (int m = 0; m<N; ++m) { \
38931     const T xc = *ptrX; ptrX+=off; \
38932     const double yc = *(ptrY++) = (double)(a0*xc + a1*xp - b1*yp - b2*yb); \
38933     xp = xc; yb = yp; yp = yc; \
38934   } \
38935   T xn = (T)0, xa = (T)0; \
38936   double yn = 0, ya = 0; \
38937   if (boundary_conditions) { xn = xa = *(ptrX - off); yn = ya = (double)coefn*xn; } \
38938   for (int n = N - 1; n>=0; --n) { \
38939     const T xc = *(ptrX-=off); \
38940     const double yc = (double)(a2*xn + a3*xa - b1*yn - b2*ya); \
38941     xa = xn; xn = xc; ya = yn; yn = yc; \
38942     *ptrX = (T)(*(--ptrY)+yc); \
38943   }
38944       const char naxis = cimg::lowercase(axis);
38945       const double nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:
38946                                                    naxis=='y'?_height:
38947                                                    naxis=='z'?_depth:_spectrum)/100;
38948       if (is_empty() || (nsigma<0.1f && !order)) return *this;
38949       const double
38950         nnsigma = nsigma<0.1f?0.1f:nsigma,
38951         alpha = 1.695f/nnsigma,
38952         ema = std::exp(-alpha),
38953         ema2 = std::exp(-2*alpha),
38954         b1 = -2*ema,
38955         b2 = ema2;
38956       double a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
38957       switch (order) {
38958       case 0 : {
38959         const double k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2);
38960         a0 = k;
38961         a1 = k*(alpha - 1)*ema;
38962         a2 = k*(alpha + 1)*ema;
38963         a3 = -k*ema2;
38964       } break;
38965       case 1 : {
38966         const double k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema);
38967         a0 = a3 = 0;
38968         a1 = k*ema;
38969         a2 = -a1;
38970       } break;
38971       case 2 : {
38972         const double
38973           ea = std::exp(-alpha),
38974           k = -(ema2 - 1)/(2*alpha*ema),
38975           kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea));
38976         a0 = kn;
38977         a1 = -kn*(1 + k*alpha)*ema;
38978         a2 = kn*(1 - k*alpha)*ema;
38979         a3 = -kn*ema2;
38980       } break;
38981       default :
38982         throw CImgArgumentException(_cimg_instance
38983                                     "deriche(): Invalid specified filter order %u "
38984                                     "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
38985                                     cimg_instance,
38986                                     order);
38987       }
38988       coefp = (a0 + a1)/(1 + b1 + b2);
38989       coefn = (a2 + a3)/(1 + b1 + b2);
38990       switch (naxis) {
38991       case 'x' : {
38992         const int N = width();
38993         const ulongT off = 1U;
38994         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
38995                                                                    _height*_depth*_spectrum>=16))
38996         cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
38997       } break;
38998       case 'y' : {
38999         const int N = height();
39000         const ulongT off = (ulongT)_width;
39001         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39002                                                                    _height*_depth*_spectrum>=16))
39003         cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
39004       } break;
39005       case 'z' : {
39006         const int N = depth();
39007         const ulongT off = (ulongT)_width*_height;
39008         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39009                                                                    _height*_depth*_spectrum>=16))
39010         cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
39011       } break;
39012       default : {
39013         const int N = spectrum();
39014         const ulongT off = (ulongT)_width*_height*_depth;
39015         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39016                                                                    _height*_depth*_spectrum>=16))
39017         cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
39018       }
39019       }
39020       return *this;
39021     }
39022 
39023     //! Apply recursive Deriche filter \newinstance.
39024     CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x',
39025                              const bool boundary_conditions=true) const {
39026       return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
39027     }
39028 
39029     // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
39030     /*
39031        \param ptr the pointer of the data
39032        \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3].
39033        \param N size of the data
39034        \param off the offset between two data point
39035        \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative
39036        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
39037        \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005).
39038     */
39039     static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off,
39040                                       const unsigned int order, const bool boundary_conditions) {
39041       double val[4] = { 0 };  // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..]
39042       const double
39043         sumsq = filter[0], sum = sumsq * sumsq,
39044         a1 = filter[1], a2 = filter[2], a3 = filter[3],
39045         scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) );
39046       double M[9]; // Triggs matrix
39047       M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2);
39048       M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1);
39049       M[2] = scaleM * a3 * (a1 + a3 * a2);
39050       M[3] = scaleM * (a1 + a3 * a2);
39051       M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1);
39052       M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.);
39053       M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2);
39054       M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3);
39055       M[8] = scaleM * a3 * (a1 + a3 * a2);
39056       switch (order) {
39057       case 0 : {
39058         const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0);
39059         for (int pass = 0; pass<2; ++pass) {
39060           if (!pass) {
39061             for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0);
39062           } else {
39063             // Apply Triggs boundary conditions
39064             const double
39065               uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3),
39066               unp  = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus;
39067             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum;
39068             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum;
39069             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum;
39070             *data = (T)val[0];
39071             data -= off;
39072             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39073           }
39074           for (int n = pass; n<N; ++n) {
39075             val[0] = (*data);
39076             if (pass) val[0] *= sum;
39077             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
39078             *data = (T)val[0];
39079             if (!pass) data += off; else data -= off;
39080             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39081           }
39082           if (!pass) data -= off;
39083         }
39084       } break;
39085       case 1 : {
39086         double x[3]; // [front,center,back]
39087         for (int pass = 0; pass<2; ++pass) {
39088           if (!pass) {
39089             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
39090             for (int k = 0; k<4; ++k) val[k] = 0;
39091           } else {
39092             // Apply Triggs boundary conditions
39093             const double
39094               unp  = val[1], unp1 = val[2], unp2 = val[3];
39095             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
39096             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
39097             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
39098             *data = (T)val[0];
39099             data -= off;
39100             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39101           }
39102           for (int n = pass; n<N - 1; ++n) {
39103             if (!pass) {
39104               x[0] = *(data + off);
39105               val[0] = 0.5f * (x[0] - x[2]);
39106             } else val[0] = (*data) * sum;
39107             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
39108             *data = (T)val[0];
39109             if (!pass) {
39110               data += off;
39111               for (int k = 2; k>0; --k) x[k] = x[k - 1];
39112             } else { data-=off;}
39113             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39114           }
39115           *data = (T)0;
39116         }
39117       } break;
39118       case 2: {
39119         double x[3]; // [front,center,back]
39120         for (int pass = 0; pass<2; ++pass) {
39121           if (!pass) {
39122             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
39123             for (int k = 0; k<4; ++k) val[k] = 0;
39124           } else {
39125             // Apply Triggs boundary conditions
39126             const double
39127               unp  = val[1], unp1 = val[2], unp2 = val[3];
39128             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
39129             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
39130             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
39131             *data = (T)val[0];
39132             data -= off;
39133             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39134           }
39135           for (int n = pass; n<N - 1; ++n) {
39136             if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); }
39137             else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; }
39138             for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k];
39139             *data = (T)val[0];
39140             if (!pass) data += off; else data -= off;
39141             for (int k = 2; k>0; --k) x[k] = x[k - 1];
39142             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39143           }
39144           *data = (T)0;
39145         }
39146       } break;
39147       case 3: {
39148         double x[3]; // [front,center,back]
39149         for (int pass = 0; pass<2; ++pass) {
39150           if (!pass) {
39151             for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
39152             for (int k = 0; k<4; ++k) val[k] = 0;
39153           } else {
39154             // Apply Triggs boundary conditions
39155             const double
39156               unp = val[1], unp1 = val[2], unp2 = val[3];
39157             val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
39158             val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
39159             val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
39160             *data = (T)val[0];
39161             data -= off;
39162             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39163           }
39164           for (int n = pass; n<N - 1; ++n) {
39165             if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); }
39166             else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; }
39167             for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
39168             *data = (T)val[0];
39169             if (!pass) data += off; else data -= off;
39170             for (int k = 2; k>0; --k) x[k] = x[k - 1];
39171             for (int k = 3; k>0; --k) val[k] = val[k - 1];
39172           }
39173           *data = (T)0;
39174         }
39175       } break;
39176       }
39177     }
39178 
39179     //! Van Vliet recursive Gaussian filter.
39180     /**
39181        \param sigma standard deviation of the Gaussian filter
39182        \param order the order of the filter 0,1,2,3
39183        \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
39184        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
39185        \note dirichlet boundary condition has a strange behavior
39186 
39187        I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering.
39188        IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002.
39189 
39190        (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995)
39191 
39192        Boundary conditions (only for order 0) using Triggs matrix, from
39193        B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet
39194        recursive filtering. IEEE Trans. Signal Processing,
39195        vol. 54, pp. 2365-2367, 2006.
39196     **/
39197     CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x',
39198                       const bool boundary_conditions=true) {
39199       if (is_empty()) return *this;
39200       if (!cimg::type<T>::is_float())
39201         return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this);
39202       const char naxis = cimg::lowercase(axis);
39203       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
39204       if (is_empty() || (nsigma<0.5f && !order)) return *this;
39205       const double
39206         nnsigma = nsigma<0.5f?0.5f:nsigma,
39207         m0 = 1.16680, m1 = 1.10783, m2 = 1.40586,
39208         m1sq = m1 * m1, m2sq = m2 * m2,
39209         q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)),
39210         qsq = q * q,
39211         scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq),
39212         b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale,
39213         b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale,
39214         b3 = -qsq * q / scale,
39215         B = ( m0 * (m1sq + m2sq) ) / scale;
39216       double filter[4];
39217       filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3;
39218       switch (naxis) {
39219       case 'x' : {
39220         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39221                                                                    _height*_depth*_spectrum>=16))
39222         cimg_forYZC(*this,y,z,c)
39223           _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
39224       } break;
39225       case 'y' : {
39226         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39227                                                                    _height*_depth*_spectrum>=16))
39228         cimg_forXZC(*this,x,z,c)
39229           _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions);
39230       } break;
39231       case 'z' : {
39232         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39233                                                                    _height*_depth*_spectrum>=16))
39234         cimg_forXYC(*this,x,y,c)
39235           _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height,
39236                                 order,boundary_conditions);
39237       } break;
39238       default : {
39239         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39240                                                                    _height*_depth*_spectrum>=16))
39241         cimg_forXYZ(*this,x,y,z)
39242           _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth,
39243                                 order,boundary_conditions);
39244       }
39245       }
39246       return *this;
39247     }
39248 
39249     //! Blur image using Van Vliet recursive Gaussian filter. \newinstance.
39250     CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
39251                               const bool boundary_conditions=true) const {
39252       return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
39253     }
39254 
39255     //! Blur image.
39256     /**
39257        \param sigma_x Standard deviation of the blur, along the X-axis.
39258        \param sigma_y Standard deviation of the blur, along the Y-axis.
39259        \param sigma_z Standard deviation of the blur, along the Z-axis.
39260        \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
39261        \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel.
39262        \note
39263        - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian).
39264        - This is a recursive algorithm, not depending on the values of the standard deviations.
39265        \see deriche(), vanvliet().
39266     **/
39267     CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
39268                   const bool boundary_conditions=true, const bool is_gaussian=true) {
39269       if (is_empty()) return *this;
39270       if (is_gaussian) {
39271         if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
39272         if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
39273         if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
39274       } else {
39275         if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
39276         if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
39277         if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
39278       }
39279       return *this;
39280     }
39281 
39282     //! Blur image \newinstance.
39283     CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
39284                           const bool boundary_conditions=true, const bool is_gaussian=true) const {
39285       return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
39286     }
39287 
39288     //! Blur image isotropically.
39289     /**
39290        \param sigma Standard deviation of the blur.
39291        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
39292        \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise.
39293        \see deriche(), vanvliet().
39294     **/
39295     CImg<T>& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) {
39296       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
39297       return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
39298     }
39299 
39300     //! Blur image isotropically \newinstance.
39301     CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) const {
39302       return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
39303     }
39304 
39305     //! Blur image anisotropically, directed by a field of diffusion tensors.
39306     /**
39307        \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
39308        \param amplitude Amplitude of the smoothing.
39309        \param dl Spatial discretization.
39310        \param da Angular discretization.
39311        \param gauss_prec Precision of the diffusion process.
39312        \param interpolation_type Interpolation scheme.
39313          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
39314        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
39315     **/
39316     template<typename t>
39317     CImg<T>& blur_anisotropic(const CImg<t>& G,
39318                               const float amplitude=60, const float dl=0.8f, const float da=30,
39319                               const float gauss_prec=2, const unsigned int interpolation_type=0,
39320                               const bool is_fast_approx=1) {
39321 
39322       // Check arguments and init variables
39323       if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
39324         throw CImgArgumentException(_cimg_instance
39325                                     "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
39326                                     cimg_instance,
39327                                     G._width,G._height,G._depth,G._spectrum,G._data);
39328       if (is_empty() || dl<0) return *this;
39329       const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100;
39330       unsigned int iamplitude = cimg::round(namplitude);
39331       const bool is_3d = (G._spectrum==6);
39332       T val_min, val_max = max_min(val_min);
39333       _cimg_abort_init_openmp;
39334       cimg_abort_init;
39335 
39336       if (da<=0) {  // Iterated oriented Laplacians
39337         CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
39338         for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) {
39339           Tfloat *ptrd = velocity._data, veloc_max = 0;
39340           if (is_3d) // 3D version
39341             cimg_forC(*this,c) {
39342               cimg_abort_test;
39343               CImg_3x3x3(I,Tfloat);
39344               cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
39345                 const Tfloat
39346                   ixx = Incc + Ipcc - 2*Iccc,
39347                   ixy = (Innc + Ippc - Inpc - Ipnc)/4,
39348                   ixz = (Incn + Ipcp - Incp - Ipcn)/4,
39349                   iyy = Icnc + Icpc - 2*Iccc,
39350                   iyz = (Icnn + Icpp - Icnp - Icpn)/4,
39351                   izz = Iccn + Iccp - 2*Iccc,
39352                   veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
39353                                    G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
39354                 *(ptrd++) = veloc;
39355                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
39356               }
39357             }
39358           else // 2D version
39359             cimg_forZC(*this,z,c) {
39360               cimg_abort_test;
39361               CImg_3x3(I,Tfloat);
39362               cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
39363                 const Tfloat
39364                   ixx = Inc + Ipc - 2*Icc,
39365                   ixy = (Inn + Ipp - Inp - Ipn)/4,
39366                   iyy = Icn + Icp - 2*Icc,
39367                   veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
39368                 *(ptrd++) = veloc;
39369                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
39370               }
39371             }
39372           if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
39373         }
39374       } else { // LIC-based smoothing
39375         const ulongT whd = (ulongT)_width*_height*_depth;
39376         const float sqrt2amplitude = (float)std::sqrt(2*namplitude);
39377         const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
39378         CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
39379         int N = 0;
39380         if (is_3d) { // 3D version
39381           for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) {
39382             const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
39383               da2 = datmp<1?360.f:datmp;
39384             for (float theta = 0; theta<360; (theta+=da2),++N) {
39385               const float
39386                 thetar = (float)(theta*cimg::PI/180),
39387                 vx = (float)(std::cos(thetar)*std::cos(phir)),
39388                 vy = (float)(std::sin(thetar)*std::cos(phir)),
39389                 vz = (float)std::sin(phir);
39390               const t
39391                 *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
39392                 *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
39393               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);
39394               cimg_forXYZ(G,xg,yg,zg) {
39395                 const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
39396                 const float
39397                   u = (float)(a*vx + b*vy + c*vz),
39398                   v = (float)(b*vx + d*vy + e*vz),
39399                   w = (float)(c*vx + e*vy + f*vz),
39400                   n = 1e-5f + cimg::hypot(u,v,w),
39401                   dln = dl/n;
39402                 *(pd0++) = (Tfloat)(u*dln);
39403                 *(pd1++) = (Tfloat)(v*dln);
39404                 *(pd2++) = (Tfloat)(w*dln);
39405                 *(pd3++) = (Tfloat)n;
39406               }
39407 
39408               cimg_abort_test;
39409               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
39410                                  cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2)
39411                                  firstprivate(val))
39412               cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 {
39413                 cimg_abort_test2;
39414                 cimg_forX(*this,x) {
39415                   val.fill(0);
39416                   const float
39417                     n = (float)W(x,y,z,3),
39418                     fsigma = (float)(n*sqrt2amplitude),
39419                     fsigma2 = 2*fsigma*fsigma,
39420                     length = gauss_prec*fsigma;
39421                   float
39422                     S = 0,
39423                     X = (float)x,
39424                     Y = (float)y,
39425                     Z = (float)z;
39426                   switch (interpolation_type) {
39427                   case 0 : { // Nearest neighbor
39428                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
39429                       const int
39430                         cx = (int)(X + 0.5f),
39431                         cy = (int)(Y + 0.5f),
39432                         cz = (int)(Z + 0.5f);
39433                       const float
39434                         u = (float)W(cx,cy,cz,0),
39435                         v = (float)W(cx,cy,cz,1),
39436                         w = (float)W(cx,cy,cz,2);
39437                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
39438                       else {
39439                         const float coef = (float)std::exp(-l*l/fsigma2);
39440                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
39441                         S+=coef;
39442                       }
39443                       X+=u; Y+=v; Z+=w;
39444                     }
39445                   } break;
39446                   case 1 : { // Linear interpolation
39447                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
39448                       const float
39449                         u = (float)(W._linear_atXYZ(X,Y,Z,0)),
39450                         v = (float)(W._linear_atXYZ(X,Y,Z,1)),
39451                         w = (float)(W._linear_atXYZ(X,Y,Z,2));
39452                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
39453                       else {
39454                         const float coef = (float)std::exp(-l*l/fsigma2);
39455                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
39456                         S+=coef;
39457                       }
39458                       X+=u; Y+=v; Z+=w;
39459                     }
39460                   } break;
39461                   default : { // 2nd order Runge Kutta
39462                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
39463                       const float
39464                         u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
39465                         v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
39466                         w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
39467                         u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
39468                         v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
39469                         w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
39470                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
39471                       else {
39472                         const float coef = (float)std::exp(-l*l/fsigma2);
39473                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
39474                         S+=coef;
39475                       }
39476                       X+=u; Y+=v; Z+=w;
39477                     }
39478                   } break;
39479                   }
39480                   Tfloat *ptrd = res.data(x,y,z);
39481                   if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
39482                   else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
39483                 }
39484               } _cimg_abort_catch_openmp2
39485             }
39486           }
39487         } else { // 2D LIC algorithm
39488           for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) {
39489             const float thetar = (float)(theta*cimg::PI/180),
39490               vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
39491             const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
39492             Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
39493             cimg_forXY(G,xg,yg) {
39494               const t a = *(pa++), b = *(pb++), c = *(pc++);
39495               const float
39496                 u = (float)(a*vx + b*vy),
39497                 v = (float)(b*vx + c*vy),
39498                 n = std::max(1e-5f,cimg::hypot(u,v)),
39499                 dln = dl/n;
39500               *(pd0++) = (Tfloat)(u*dln);
39501               *(pd1++) = (Tfloat)(v*dln);
39502               *(pd2++) = (Tfloat)n;
39503             }
39504 
39505             cimg_abort_test;
39506             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2)
39507                                firstprivate(val))
39508             cimg_forY(*this,y) _cimg_abort_try_openmp2 {
39509               cimg_abort_test2;
39510               cimg_forX(*this,x) {
39511                 val.fill(0);
39512                 const float
39513                   n = (float)W(x,y,0,2),
39514                   fsigma = (float)(n*sqrt2amplitude),
39515                   fsigma2 = 2*fsigma*fsigma,
39516                   length = gauss_prec*fsigma;
39517                 float
39518                   S = 0,
39519                   X = (float)x,
39520                   Y = (float)y;
39521                 switch (interpolation_type) {
39522                 case 0 : { // Nearest-neighbor
39523                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
39524                     const int
39525                       cx = (int)(X + 0.5f),
39526                       cy = (int)(Y + 0.5f);
39527                     const float
39528                       u = (float)W(cx,cy,0,0),
39529                       v = (float)W(cx,cy,0,1);
39530                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
39531                     else {
39532                       const float coef = (float)std::exp(-l*l/fsigma2);
39533                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
39534                       S+=coef;
39535                     }
39536                     X+=u; Y+=v;
39537                   }
39538                 } break;
39539                 case 1 : { // Linear interpolation
39540                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
39541                     const float
39542                       u = (float)(W._linear_atXY(X,Y,0,0)),
39543                       v = (float)(W._linear_atXY(X,Y,0,1));
39544                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
39545                     else {
39546                       const float coef = (float)std::exp(-l*l/fsigma2);
39547                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
39548                       S+=coef;
39549                     }
39550                     X+=u; Y+=v;
39551                   }
39552                 } break;
39553                 default : { // 2nd-order Runge-kutta interpolation
39554                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
39555                     const float
39556                       u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
39557                       v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
39558                       u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
39559                       v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
39560                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
39561                     else {
39562                       const float coef = (float)std::exp(-l*l/fsigma2);
39563                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
39564                       S+=coef;
39565                     }
39566                     X+=u; Y+=v;
39567                   }
39568                 }
39569                 }
39570                 Tfloat *ptrd = res.data(x,y);
39571                 if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
39572                 else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
39573               }
39574             } _cimg_abort_catch_openmp2
39575           }
39576         }
39577         const Tfloat *ptrs = res._data;
39578         cimg_for(*this,ptrd,T) {
39579           const Tfloat _val = *(ptrs++)/N;
39580           *ptrd = _val<val_min?val_min:(_val>val_max?val_max:(T)_val);
39581         }
39582       }
39583       cimg_abort_test;
39584       return *this;
39585     }
39586 
39587     //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
39588     template<typename t>
39589     CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G,
39590                                       const float amplitude=60, const float dl=0.8f, const float da=30,
39591                                       const float gauss_prec=2, const unsigned int interpolation_type=0,
39592                                       const bool is_fast_approx=true) const {
39593       return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
39594     }
39595 
39596     //! Blur image anisotropically, in an edge-preserving way.
39597     /**
39598        \param amplitude Amplitude of the smoothing.
39599        \param sharpness Sharpness.
39600        \param anisotropy Anisotropy.
39601        \param alpha Standard deviation of the gradient blur.
39602        \param sigma Standard deviation of the structure tensor blur.
39603        \param dl Spatial discretization.
39604        \param da Angular discretization.
39605        \param gauss_prec Precision of the diffusion process.
39606        \param interpolation_type Interpolation scheme.
39607          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
39608        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
39609      **/
39610     CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
39611                               const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
39612                               const float gauss_prec=2, const unsigned int interpolation_type=0,
39613                               const bool is_fast_approx=true) {
39614       const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100;
39615       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
39616       return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3),
39617                               amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
39618     }
39619 
39620     //! Blur image anisotropically, in an edge-preserving way \newinstance.
39621     CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
39622                                       const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
39623                                       const float da=30, const float gauss_prec=2,
39624                                       const unsigned int interpolation_type=0,
39625                                       const bool is_fast_approx=true) const {
39626       return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,
39627                                                         interpolation_type,is_fast_approx);
39628     }
39629 
39630     //! Blur image, with the joint bilateral filter.
39631     /**
39632        \param guide Image used to model the smoothing weights.
39633        \param sigma_x Amount of blur along the X-axis.
39634        \param sigma_y Amount of blur along the Y-axis.
39635        \param sigma_z Amount of blur along the Z-axis.
39636        \param sigma_r Amount of blur along the value axis.
39637        \param sampling_x Amount of downsampling along the X-axis used for the approximation.
39638          Defaults (0) to sigma_x.
39639        \param sampling_y Amount of downsampling along the Y-axis used for the approximation.
39640          Defaults (0) to sigma_y.
39641        \param sampling_z Amount of downsampling along the Z-axis used for the approximation.
39642          Defaults (0) to sigma_z.
39643        \param sampling_r Amount of downsampling along the value axis used for the approximation.
39644          Defaults (0) to sigma_r.
39645        \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
39646        (extended for 3D volumetric images).
39647        It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m
39648     **/
39649     template<typename t>
39650     CImg<T>& blur_bilateral(const CImg<t>& guide,
39651                             const float sigma_x, const float sigma_y,
39652                             const float sigma_z, const float sigma_r,
39653                             const float sampling_x, const float sampling_y,
39654                             const float sampling_z, const float sampling_r) {
39655       if (!is_sameXYZ(guide))
39656         throw CImgArgumentException(_cimg_instance
39657                                     "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
39658                                     cimg_instance,
39659                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
39660       if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this;
39661       T edge_min, edge_max = guide.max_min(edge_min);
39662       if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z);
39663       const float
39664         edge_delta = (float)(edge_max - edge_min),
39665         _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
39666         _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
39667         _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
39668         _sigma_r = sigma_r>=0?sigma_r:-sigma_r*edge_delta/100,
39669         _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f),
39670         _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f),
39671         _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f),
39672         _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256),
39673         derived_sigma_x = _sigma_x / _sampling_x,
39674         derived_sigma_y = _sigma_y / _sampling_y,
39675         derived_sigma_z = _sigma_z / _sampling_z,
39676         derived_sigma_r = _sigma_r / _sampling_r;
39677       const int
39678         padding_x = (int)(2*derived_sigma_x) + 1,
39679         padding_y = (int)(2*derived_sigma_y) + 1,
39680         padding_z = (int)(2*derived_sigma_z) + 1,
39681         padding_r = (int)(2*derived_sigma_r) + 1;
39682       const unsigned int
39683         bx = (unsigned int)((_width  - 1)/_sampling_x + 1 + 2*padding_x),
39684         by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y),
39685         bz = (unsigned int)((_depth  - 1)/_sampling_z + 1 + 2*padding_z),
39686         br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r);
39687       if (bx>0 || by>0 || bz>0 || br>0) {
39688         const bool is_3d = (_depth>1);
39689         if (is_3d) { // 3D version of the algorithm
39690           CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
39691           cimg_forC(*this,c) {
39692             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
39693             bgrid.fill(0); bgridw.fill(0);
39694             cimg_forXYZ(*this,x,y,z) {
39695               const T val = (*this)(x,y,z,c);
39696               const float edge = (float)_guide(x,y,z);
39697               const int
39698                 X = (int)cimg::round(x/_sampling_x) + padding_x,
39699                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
39700                 Z = (int)cimg::round(z/_sampling_z) + padding_z,
39701                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
39702               bgrid(X,Y,Z,R)+=(float)val;
39703               bgridw(X,Y,Z,R)+=1;
39704             }
39705             bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
39706             bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
39707 
39708             cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),4096))
39709             cimg_forXYZ(*this,x,y,z) {
39710               const float edge = (float)_guide(x,y,z);
39711               const float
39712                 X = x/_sampling_x + padding_x,
39713                 Y = y/_sampling_y + padding_y,
39714                 Z = z/_sampling_z + padding_z,
39715                 R = (edge - edge_min)/_sampling_r + padding_r;
39716               const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
39717               (*this)(x,y,z,c) = (T)(bval0/bval1);
39718             }
39719           }
39720         } else { // 2D version of the algorithm
39721           CImg<floatT> bgrid(bx,by,br,2);
39722           cimg_forC(*this,c) {
39723             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
39724             bgrid.fill(0);
39725             cimg_forXY(*this,x,y) {
39726               const T val = (*this)(x,y,c);
39727               const float edge = (float)_guide(x,y);
39728               const int
39729                 X = (int)cimg::round(x/_sampling_x) + padding_x,
39730                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
39731                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
39732               bgrid(X,Y,R,0)+=(float)val;
39733               bgrid(X,Y,R,1)+=1;
39734             }
39735             bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false);
39736 
39737             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(size(),4096))
39738             cimg_forXY(*this,x,y) {
39739               const float edge = (float)_guide(x,y);
39740               const float
39741                 X = x/_sampling_x + padding_x,
39742                 Y = y/_sampling_y + padding_y,
39743                 R = (edge - edge_min)/_sampling_r + padding_r;
39744               const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
39745               (*this)(x,y,c) = (T)(bval0/bval1);
39746             }
39747           }
39748         }
39749       }
39750       return *this;
39751     }
39752 
39753     //! Blur image, with the joint bilateral filter \newinstance.
39754     template<typename t>
39755     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
39756                                     const float sigma_x, const float sigma_y,
39757                                     const float sigma_z, const float sigma_r,
39758                                     const float sampling_x, const float sampling_y,
39759                                     const float sampling_z, const float sampling_r) const {
39760       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,
39761                                                       sampling_x,sampling_y,sampling_z,sampling_r);
39762     }
39763 
39764     //! Blur image using the joint bilateral filter.
39765     /**
39766        \param guide Image used to model the smoothing weights.
39767        \param sigma_s Amount of blur along the XYZ-axes.
39768        \param sigma_r Amount of blur along the value axis.
39769        \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s.
39770        \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r.
39771     **/
39772     template<typename t>
39773     CImg<T>& blur_bilateral(const CImg<t>& guide,
39774                             const float sigma_s, const float sigma_r,
39775                             const float sampling_s=0, const float sampling_r=0) {
39776       const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
39777       return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r);
39778     }
39779 
39780     //! Blur image using the bilateral filter \newinstance.
39781     template<typename t>
39782     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
39783                                     const float sigma_s, const float sigma_r,
39784                                     const float sampling_s=0, const float sampling_r=0) const {
39785       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
39786     }
39787 
39788     // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()).
39789     /*
39790       \param ptr the pointer of the data
39791       \param N size of the data
39792       \param boxsize Size of the box filter (can be subpixel).
39793       \param off the offset between two data point
39794       \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative.
39795       \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
39796     */
39797     static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off,
39798                                      const int order, const bool boundary_conditions,
39799                                      const unsigned int nb_iter) {
39800       // Smooth.
39801       if (boxsize>1 && nb_iter) {
39802         const int w2 = (int)(boxsize - 1)/2;
39803         const unsigned int winsize = 2*w2 + 1U;
39804         const double frac = (boxsize - winsize)/2.;
39805         CImg<T> win(winsize);
39806         for (unsigned int iter = 0; iter<nb_iter; ++iter) {
39807           Tdouble sum = 0; // window sum
39808           for (int x = -w2; x<=w2; ++x) {
39809             win[x + w2] = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x);
39810             sum+=win[x + w2];
39811           }
39812           int ifirst = 0, ilast = 2*w2;
39813           T
39814             prev = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-w2 - 1),
39815             next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,w2 + 1);
39816           for (int x = 0; x < N - 1; ++x) {
39817             const double sum2 = sum + frac * (prev + next);
39818             ptr[x*off] = (T)(sum2/boxsize);
39819             prev = win[ifirst];
39820             sum-=prev;
39821             ifirst = (int)((ifirst + 1)%winsize);
39822             ilast = (int)((ilast + 1)%winsize);
39823             win[ilast] = next;
39824             sum+=next;
39825             next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + w2 + 2);
39826           }
39827           const double sum2 = sum + frac * (prev + next);
39828           ptr[(N - 1)*off] = (T)(sum2/boxsize);
39829         }
39830       }
39831 
39832       // Derive.
39833       switch (order) {
39834       case 0 :
39835         break;
39836       case 1 : {
39837         Tfloat
39838           p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
39839           c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
39840           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
39841         for (int x = 0; x<N - 1; ++x) {
39842           ptr[x*off] = (T)((n-p)/2.);
39843           p = c;
39844           c = n;
39845           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
39846         }
39847         ptr[(N - 1)*off] = (T)((n-p)/2.);
39848       } break;
39849       case 2: {
39850         Tfloat
39851           p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
39852           c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
39853           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
39854         for (int x = 0; x<N - 1; ++x) {
39855           ptr[x*off] = (T)(n - 2*c + p);
39856           p = c;
39857           c = n;
39858           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
39859         }
39860         ptr[(N - 1)*off] = (T)(n - 2*c + p);
39861       } break;
39862       }
39863     }
39864 
39865     static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off,
39866                                    const bool boundary_conditions, const int x) {
39867       if (x<0) return boundary_conditions?ptr[0]:T();
39868       if (x>=N) return boundary_conditions?ptr[(N - 1)*off]:T();
39869       return ptr[x*off];
39870     }
39871 
39872     // Apply box filter of order 0,1,2.
39873     /**
39874       \param boxsize Size of the box window (can be subpixel)
39875       \param order the order of the filter 0,1 or 2.
39876       \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
39877       \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
39878       \param nb_iter Number of filter iterations.
39879     **/
39880     CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x',
39881                        const bool boundary_conditions=true,
39882                        const unsigned int nb_iter=1) {
39883       if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this;
39884       const char naxis = cimg::lowercase(axis);
39885       const float nboxsize = boxsize>=0?boxsize:-boxsize*
39886         (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
39887       switch (naxis) {
39888       case 'x' : {
39889         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39890                                                                    _height*_depth*_spectrum>=16))
39891         cimg_forYZC(*this,y,z,c)
39892           _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter);
39893       } break;
39894       case 'y' : {
39895         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39896                                                                    _height*_depth*_spectrum>=16))
39897         cimg_forXZC(*this,x,z,c)
39898           _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter);
39899       } break;
39900       case 'z' : {
39901         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39902                                                                    _height*_depth*_spectrum>=16))
39903         cimg_forXYC(*this,x,y,c)
39904           _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter);
39905       } break;
39906       default : {
39907         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
39908                                                                    _height*_depth*_spectrum>=16))
39909         cimg_forXYZ(*this,x,y,z)
39910           _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth,
39911                                order,boundary_conditions,nb_iter);
39912       }
39913       }
39914       return *this;
39915     }
39916 
39917     // Apply box filter of order 0,1 or 2 \newinstance.
39918     CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x',
39919                                const bool boundary_conditions=true,
39920                                const unsigned int nb_iter=1) const {
39921       return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter);
39922     }
39923 
39924     //! Blur image with a box filter.
39925     /**
39926        \param boxsize_x Size of the box window, along the X-axis (can be subpixel).
39927        \param boxsize_y Size of the box window, along the Y-axis (can be subpixel).
39928        \param boxsize_z Size of the box window, along the Z-axis (can be subpixel).
39929        \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
39930        \param nb_iter Number of filter iterations.
39931        \note
39932        - This is a recursive algorithm, not depending on the values of the box kernel size.
39933        \see blur().
39934     **/
39935     CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
39936                       const bool boundary_conditions=true,
39937                       const unsigned int nb_iter=1) {
39938       if (is_empty()) return *this;
39939       if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter);
39940       if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter);
39941       if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter);
39942       return *this;
39943     }
39944 
39945     //! Blur image with a box filter \newinstance.
39946     CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
39947                               const bool boundary_conditions=true) const {
39948       return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions);
39949     }
39950 
39951     //! Blur image with a box filter.
39952     /**
39953        \param boxsize Size of the box window (can be subpixel).
39954        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
39955        \see deriche(), vanvliet().
39956     **/
39957     CImg<T>& blur_box(const float boxsize, const bool boundary_conditions=true) {
39958       const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100;
39959       return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions);
39960     }
39961 
39962     //! Blur image with a box filter \newinstance.
39963     CImg<Tfloat> get_blur_box(const float boxsize, const bool boundary_conditions=true) const {
39964       return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions);
39965     }
39966 
39967     //! Blur image, with the image guided filter.
39968     /**
39969        \param guide Image used to guide the smoothing process.
39970        \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size.
39971        \param regularization Regularization parameter.
39972                              If negative, it is expressed as a percentage of the guide value range.
39973        \note This method implements the filtering algorithm described in:
39974        He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence,
39975        IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013
39976     **/
39977     template<typename t>
39978     CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) {
39979       return get_blur_guided(guide,radius,regularization).move_to(*this);
39980     }
39981 
39982     //! Blur image, with the image guided filter \newinstance.
39983     template<typename t>
39984     CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const {
39985       if (!is_sameXYZ(guide))
39986         throw CImgArgumentException(_cimg_instance
39987                                     "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
39988                                     cimg_instance,
39989                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
39990       if (is_empty() || !radius) return *this;
39991       const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100);
39992       float _regularization = regularization;
39993       if (regularization<0) {
39994         T edge_min, edge_max = guide.max_min(edge_min);
39995         if (edge_min==edge_max) return *this;
39996         _regularization = -regularization*(edge_max - edge_min)/100;
39997       }
39998       _regularization = std::max(_regularization,0.01f);
39999       const unsigned int psize = (unsigned int)(1 + 2*_radius);
40000       CImg<Tfloat>
40001         mean_p = get_blur_box(psize,true),
40002         mean_I = guide.get_blur_box(psize,true).resize(mean_p),
40003         cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I),
40004         var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(),
40005         &a = cov_Ip.div(var_I+=_regularization),
40006         &b = mean_p-=a.get_mul(mean_I);
40007       a.blur_box(psize,true);
40008       b.blur_box(psize,true);
40009       return a.mul(guide)+=b;
40010     }
40011 
40012     //! Blur image using patch-based space.
40013     /**
40014        \param guide Image used to model the smoothing weights.
40015        \param sigma_s Amount of blur along the XYZ-axes.
40016        \param sigma_r Amount of blur along the value axis.
40017        \param patch_size Size of the patches.
40018        \param lookup_size Size of the window to search similar patches.
40019        \param smoothness Smoothness for the patch comparison.
40020        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
40021     **/
40022     template<typename t>
40023     CImg<T>& blur_patch(const CImg<t>& guide,
40024                         const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
40025                         const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
40026       if (is_empty() || !patch_size || !lookup_size) return *this;
40027       return get_blur_patch(guide,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
40028     }
40029 
40030     //! Blur image using patch-based space \newinstance.
40031     template<typename t>
40032     CImg<Tfloat> get_blur_patch(const CImg<t>& guide,
40033                                 const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
40034                                 const unsigned int lookup_size=4, const float smoothness=0,
40035                                 const bool is_fast_approx=true) const {
40036 
40037 #define _cimg_blur_patch3d_fast(N) { \
40038       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
40039                          cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
40040                          firstprivate(P,Q)) \
40041       cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \
40042         cimg_abort_test; \
40043         cimg_def##N##x##N##x##N(res,x,y,z); \
40044         tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
40045         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
40046                   x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
40047         tfloat sum_weights = 0; \
40048         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \
40049           if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) { \
40050             tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
40051             tfloat distance2 = 0; \
40052             pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
40053             distance2/=Pnorm; \
40054             const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
40055               alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0:1; \
40056             sum_weights+=weight; \
40057             cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
40058           } \
40059         if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
40060         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
40061     } _cimg_abort_catch_openmp }
40062 
40063 #define _cimg_blur_patch3d(N) { \
40064       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
40065                          cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
40066                          firstprivate(P,Q)) \
40067       cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \
40068         cimg_abort_test; \
40069         cimg_def##N##x##N##x##N(res,x,y,z); \
40070         tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
40071         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
40072                   x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
40073         tfloat sum_weights = 0, weight_max = 0; \
40074         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
40075           tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
40076           tfloat distance2 = 0; \
40077           pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
40078           distance2/=Pnorm; \
40079           const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
40080             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = std::exp(-alldist); \
40081           if (weight>weight_max) weight_max = weight; \
40082           sum_weights+=weight; \
40083           cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
40084         } \
40085         sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \
40086         if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
40087         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
40088       } _cimg_abort_catch_openmp }
40089 
40090 #define _cimg_blur_patch2d_fast(N) { \
40091         cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
40092                            firstprivate(P,Q)) \
40093         cimg_forXY(res,x,y) _cimg_abort_try_openmp { \
40094           cimg_abort_test; \
40095           cimg_def##N##x##N(res,x,y); \
40096           tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
40097           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
40098           tfloat sum_weights = 0; \
40099           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \
40100             if (cimg::abs(_guide(x,y,0,0) - _guide(p,q,0,0))<sigma_r3) { \
40101               tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
40102               tfloat distance2 = 0; \
40103               pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
40104               distance2/=Pnorm; \
40105               const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
40106                 alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0:1; \
40107               sum_weights+=weight; \
40108               cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
40109             } \
40110           if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
40111           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
40112         } _cimg_abort_catch_openmp }
40113 
40114 #define _cimg_blur_patch2d(N) { \
40115         cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
40116                            firstprivate(P,Q)) \
40117         cimg_forXY(res,x,y) _cimg_abort_try_openmp { \
40118           cimg_abort_test; \
40119           cimg_def##N##x##N(res,x,y); \
40120           tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
40121           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
40122           tfloat sum_weights = 0, weight_max = 0; \
40123           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
40124             tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
40125             tfloat distance2 = 0; \
40126             pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
40127             distance2/=Pnorm; \
40128             const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
40129               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = std::exp(-alldist); \
40130             if (weight>weight_max) weight_max = weight; \
40131             sum_weights+=weight; \
40132             cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
40133           } \
40134           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \
40135           if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
40136           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
40137     } _cimg_abort_catch_openmp }
40138 
40139       typedef _cimg_tfloat tfloat;
40140       if (!is_sameXYZ(guide))
40141         throw CImgArgumentException(_cimg_instance
40142                                     "blur_patch(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
40143                                     cimg_instance,
40144                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
40145       if (is_empty() || !patch_size || !lookup_size) return +*this;
40146       Tfloat val_min, val_max = (Tfloat)max_min(val_min);
40147       _cimg_abort_init_openmp;
40148       cimg_abort_init;
40149 
40150       CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
40151       const CImg<tfloat>
40152         __guide = guide?CImg<tfloat>(guide,guide.pixel_type()==cimg::type<tfloat>::string()):
40153                         CImg<tfloat>(*this,pixel_type()==cimg::type<tfloat>::string()),
40154         _guide = smoothness>0?__guide.get_blur(smoothness):__guide.get_shared();
40155       CImg<tfloat> P(_guide._spectrum*patch_size*patch_size*(_depth>1?patch_size:1)), Q(P);
40156 
40157       t guide_min = (t)0, guide_max = (t)0;
40158       if (sigma_r<0) guide_max = guide.max_min(guide_min);
40159       const float
40160         guide_delta = (float)(guide_max - guide_min),
40161         _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
40162         _sigma_r = sigma_r>=0?sigma_r:-sigma_r*guide_delta/100,
40163         sigma_s2 = _sigma_s*_sigma_s,
40164         sigma_r2 = _sigma_r*_sigma_r,
40165         sigma_r3 = 3*_sigma_r,
40166         Pnorm = P.size()*sigma_r2;
40167       const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
40168       const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
40169       cimg::unused(N2,N3);
40170       if (_depth>1) switch (patch_size) { // 3D
40171         case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
40172         case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
40173         default : {
40174           const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
40175           if (is_fast_approx) {
40176             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40177                                cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
40178                                firstprivate(P,Q))
40179             cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Fast
40180               cimg_abort_test;
40181               P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
40182               const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
40183                 x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
40184               tfloat sum_weights = 0;
40185               cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r)
40186                 if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) {
40187                   (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
40188                   const tfloat
40189                     dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
40190                     distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
40191                     weight = distance2>3?0:1;
40192                   sum_weights+=weight;
40193                   cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
40194                 }
40195               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
40196               else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
40197             } _cimg_abort_catch_openmp
40198           } else {
40199             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40200                                cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
40201                                firstprivate(P,Q))
40202             cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Exact
40203               cimg_abort_test;
40204               P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
40205               const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
40206                         x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
40207               tfloat sum_weights = 0, weight_max = 0;
40208               cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
40209                 (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
40210                 const tfloat
40211                   dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
40212                   distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
40213                   weight = std::exp(-distance2);
40214                 if (weight>weight_max) weight_max = weight;
40215                 sum_weights+=weight;
40216                 cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
40217               }
40218               sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c);
40219               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
40220               else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
40221             } _cimg_abort_catch_openmp
40222           }
40223         }
40224         } else switch (patch_size) { // 2D
40225         case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
40226         case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
40227         case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
40228         case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
40229         case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
40230         case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
40231         case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
40232         case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
40233         default : { // Fast
40234           const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
40235           if (is_fast_approx) {
40236             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
40237                                firstprivate(P,Q))
40238             cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Fast
40239               cimg_abort_test;
40240               P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
40241               const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
40242               tfloat sum_weights = 0;
40243               cimg_for_inXY(res,x0,y0,x1,y1,p,q)
40244                 if (cimg::abs(_guide(x,y,0) - _guide(p,q,0))<sigma_r3) {
40245                   (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
40246                   const tfloat
40247                     dx = (tfloat)x - p, dy = (tfloat)y - q,
40248                     distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
40249                     weight = distance2>3?0:1;
40250                   sum_weights+=weight;
40251                   cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
40252                 }
40253               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
40254               else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
40255             } _cimg_abort_catch_openmp
40256           } else {
40257             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
40258                                firstprivate(P,Q))
40259             cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Exact
40260               cimg_abort_test;
40261               P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
40262               const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
40263               tfloat sum_weights = 0, weight_max = 0;
40264               cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
40265                 (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
40266                 const tfloat
40267                   dx = (tfloat)x - p, dy = (tfloat)y - q,
40268                   distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
40269                   weight = std::exp(-distance2);
40270                 if (weight>weight_max) weight_max = weight;
40271                 sum_weights+=weight;
40272                 cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
40273               }
40274               sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c);
40275               if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
40276               else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
40277             } _cimg_abort_catch_openmp
40278           }
40279         }
40280         }
40281       return res.cut(val_min,val_max);
40282     }
40283 
40284     //! Blur image using patch-based space \simplification.
40285     CImg<T>& blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
40286                         const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
40287       return blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
40288     }
40289 
40290     //! Blur image using patch-based space \simplification \newinstance.
40291     CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
40292                                 const unsigned int lookup_size=4, const float smoothness=0,
40293                                 const bool is_fast_approx=true) const {
40294       return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
40295     }
40296 
40297     //! Blur image with the median filter.
40298     /**
40299        \param n Size of the median filter.
40300        \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation.
40301     **/
40302     CImg<T>& blur_median(const unsigned int n, const float threshold=0) {
40303       if (!n) return *this;
40304       return get_blur_median(n,threshold).move_to(*this);
40305     }
40306 
40307     //! Blur image with the median filter \newinstance.
40308     CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const {
40309       if (is_empty() || n<=1) return +*this;
40310       CImg<T> res(_width,_height,_depth,_spectrum);
40311       T *ptrd = res._data;
40312       cimg::unused(ptrd);
40313       const int hr = (int)n/2, hl = n - hr - 1;
40314       if (res._depth!=1) { // 3D
40315         if (threshold>0)
40316           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
40317                                                                      _height*_depth*_spectrum>=4))
40318           cimg_forXYZC(*this,x,y,z,c) { // With threshold
40319             const int
40320               x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
40321               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
40322               nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
40323             const Tfloat val0 = (Tfloat)(*this)(x,y,z,c);
40324             CImg<T> values(n*n*n);
40325             unsigned int nb_values = 0;
40326             T *_ptrd = values.data();
40327             cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r)
40328               if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,r,c); ++nb_values; }
40329             res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c);
40330           }
40331         else
40332           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
40333                                                                      _height*_depth*_spectrum>=4))
40334           cimg_forXYZC(*this,x,y,z,c) { // Without threshold
40335             const int
40336               x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
40337               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
40338               nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
40339             res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
40340           }
40341       } else {
40342         if (threshold>0)
40343           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
40344                                                                      _height*_spectrum>=4))
40345           cimg_forXYC(*this,x,y,c) { // With threshold
40346             const int
40347               x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
40348               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
40349                                         nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
40350             const Tfloat val0 = (Tfloat)(*this)(x,y,c);
40351             CImg<T> values(n*n);
40352             unsigned int nb_values = 0;
40353             T *_ptrd = values.data();
40354             cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q)
40355               if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,c); ++nb_values; }
40356             res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c);
40357           }
40358         else {
40359           const int
40360             w1 = width() - 1, h1 = height() - 1,
40361             w2 = width() - 2, h2 = height() - 2,
40362             w3 = width() - 3, h3 = height() - 3,
40363             w4 = width() - 4, h4 = height() - 4;
40364           switch (n) { // Without threshold
40365           case 3 : {
40366             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
40367             cimg_forC(*this,c) {
40368               CImg<T> I(9);
40369               cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T)
40370                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]);
40371               cimg_for_borderXY(*this,x,y,1)
40372                 res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c,
40373                                       std::min(w1,x + 1),std::min(h1,y + 1),0,c).median();
40374             }
40375           } break;
40376           case 5 : {
40377             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
40378             cimg_forC(*this,c) {
40379               CImg<T> I(25);
40380               cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T)
40381                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],
40382                                           I[5],I[6],I[7],I[8],I[9],
40383                                           I[10],I[11],I[12],I[13],I[14],
40384                                           I[15],I[16],I[17],I[18],I[19],
40385                                           I[20],I[21],I[22],I[23],I[24]);
40386               cimg_for_borderXY(*this,x,y,2)
40387                 res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c,
40388                                       std::min(w1,x + 2),std::min(h1,y + 2),0,c).median();
40389             }
40390           } break;
40391           case 7 : {
40392             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
40393             cimg_forC(*this,c) {
40394               CImg<T> I(49);
40395               cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T)
40396                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],
40397                                           I[7],I[8],I[9],I[10],I[11],I[12],I[13],
40398                                           I[14],I[15],I[16],I[17],I[18],I[19],I[20],
40399                                           I[21],I[22],I[23],I[24],I[25],I[26],I[27],
40400                                           I[28],I[29],I[30],I[31],I[32],I[33],I[34],
40401                                           I[35],I[36],I[37],I[38],I[39],I[40],I[41],
40402                                           I[42],I[43],I[44],I[45],I[46],I[47],I[48]);
40403               cimg_for_borderXY(*this,x,y,3)
40404                 res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c,
40405                                       std::min(w1,x + 3),std::min(h1,y + 3),0,c).median();
40406             }
40407           } break;
40408           default : {
40409             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40410                                cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && _height*_spectrum>=4))
40411             cimg_forXYC(*this,x,y,c) {
40412               const int
40413                 x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
40414                 nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
40415                                           nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
40416               res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
40417             }
40418           }
40419           }
40420         }
40421       }
40422       return res;
40423     }
40424 
40425     //! Sharpen image.
40426     /**
40427        \param amplitude Sharpening amplitude
40428        \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>.
40429        \param edge Edge threshold (shock filters only).
40430        \param alpha Gradient smoothness (shock filters only).
40431        \param sigma Tensor smoothness (shock filters only).
40432     **/
40433     CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
40434                      const float alpha=0, const float sigma=0) {
40435       if (is_empty()) return *this;
40436       T val_min, val_max = max_min(val_min);
40437       const float nedge = edge/2;
40438       CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum);
40439 
40440       if (_depth>1) { // 3D
40441         if (sharpen_type) { // Shock filters
40442           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
40443           if (sigma>0) G.blur(sigma);
40444           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
40445                                                                      _height*_depth>=16))
40446           cimg_forYZ(G,y,z) {
40447             Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1),
40448               *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3);
40449             CImg<Tfloat> val, vec;
40450             cimg_forX(G,x) {
40451               G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
40452               if (val[0]<0) val[0] = 0;
40453               if (val[1]<0) val[1] = 0;
40454               if (val[2]<0) val[2] = 0;
40455               *(ptrG0++) = vec(0,0);
40456               *(ptrG1++) = vec(0,1);
40457               *(ptrG2++) = vec(0,2);
40458               *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge);
40459             }
40460           }
40461           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
40462                                                          _spectrum>=2))
40463           cimg_forC(*this,c) {
40464             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
40465             CImg_3x3x3(I,Tfloat);
40466             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
40467               const Tfloat
40468                 u = G(x,y,z,0),
40469                 v = G(x,y,z,1),
40470                 w = G(x,y,z,2),
40471                 amp = G(x,y,z,3),
40472                 ixx = Incc + Ipcc - 2*Iccc,
40473                 ixy = (Innc + Ippc - Inpc - Ipnc)/4,
40474                 ixz = (Incn + Ipcp - Incp - Ipcn)/4,
40475                 iyy = Icnc + Icpc - 2*Iccc,
40476                 iyz = (Icnn + Icpp - Icnp - Icpn)/4,
40477                 izz = Iccn + Iccp - 2*Iccc,
40478                 ixf = Incc - Iccc,
40479                 ixb = Iccc - Ipcc,
40480                 iyf = Icnc - Iccc,
40481                 iyb = Iccc - Icpc,
40482                 izf = Iccn - Iccc,
40483                 izb = Iccc - Iccp,
40484                 itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
40485                 it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
40486                 veloc = -amp*cimg::sign(itt)*cimg::abs(it);
40487               *(ptrd++) = veloc;
40488               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
40489             }
40490             _veloc_max[c] = veloc_max;
40491           }
40492         } else  // Inverse diffusion
40493           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
40494                                                          _spectrum>=2))
40495           cimg_forC(*this,c) {
40496             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
40497             CImg_3x3x3(I,Tfloat);
40498             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
40499               const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
40500               *(ptrd++) = veloc;
40501               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
40502             }
40503             _veloc_max[c] = veloc_max;
40504           }
40505       } else { // 2D
40506         if (sharpen_type) { // Shock filters
40507           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
40508           if (sigma>0) G.blur(sigma);
40509           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
40510                                                          _height>=(cimg_openmp_sizefactor)*16))
40511           cimg_forY(G,y) {
40512             CImg<Tfloat> val, vec;
40513             Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2);
40514             cimg_forX(G,x) {
40515               G.get_tensor_at(x,y).symmetric_eigen(val,vec);
40516               if (val[0]<0) val[0] = 0;
40517               if (val[1]<0) val[1] = 0;
40518               *(ptrG0++) = vec(0,0);
40519               *(ptrG1++) = vec(0,1);
40520               *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
40521             }
40522           }
40523           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
40524                                                          _spectrum>=2))
40525           cimg_forC(*this,c) {
40526             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
40527             CImg_3x3(I,Tfloat);
40528             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
40529               const Tfloat
40530                 u = G(x,y,0),
40531                 v = G(x,y,1),
40532                 amp = G(x,y,2),
40533                 ixx = Inc + Ipc - 2*Icc,
40534                 ixy = (Inn + Ipp - Inp - Ipn)/4,
40535                 iyy = Icn + Icp - 2*Icc,
40536                 ixf = Inc - Icc,
40537                 ixb = Icc - Ipc,
40538                 iyf = Icn - Icc,
40539                 iyb = Icc - Icp,
40540                 itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
40541                 it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
40542                 veloc = -amp*cimg::sign(itt)*cimg::abs(it);
40543               *(ptrd++) = veloc;
40544               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
40545             }
40546             _veloc_max[c] = veloc_max;
40547           }
40548         } else // Inverse diffusion
40549           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
40550                                                          _spectrum>=2))
40551           cimg_forC(*this,c) {
40552             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
40553             CImg_3x3(I,Tfloat);
40554             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
40555               const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
40556               *(ptrd++) = veloc;
40557               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
40558             }
40559             _veloc_max[c] = veloc_max;
40560           }
40561       }
40562       const Tfloat veloc_max = _veloc_max.max();
40563       if (veloc_max<=0) return *this;
40564       return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
40565     }
40566 
40567     //! Sharpen image \newinstance.
40568     CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
40569                         const float alpha=0, const float sigma=0) const {
40570       return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
40571     }
40572 
40573     //! Return image gradient.
40574     /**
40575        \param axes Axes considered for the gradient computation, as a C-string (e.g "xy").
40576        \param scheme = Numerical scheme used for the gradient computation:
40577        - -1 = Backward finite differences
40578        - 0 = Centered finite differences (default)
40579        - 1 = Forward finite differences
40580        - 2 = Using Sobel kernels
40581        - 3 = Using rotation invariant kernels
40582        - 4 = Using Deriche recursive filter.
40583        - 5 = Using Van Vliet recursive filter.
40584     **/
40585     CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=0) const {
40586       CImgList<Tfloat> res;
40587       char __axes[4] = { 0 };
40588       const char *_axes = axes?axes:__axes;
40589       if (!axes) {
40590         unsigned int k = 0;
40591         if (_width>1) __axes[k++] = 'x';
40592         if (_height>1) __axes[k++] = 'y';
40593         if (_depth>1) __axes[k++] = 'z';
40594       }
40595 
40596       CImg<Tfloat> grad;
40597       while (*_axes) {
40598         const char axis = cimg::lowercase(*(_axes++));
40599         if (axis!='x' && axis!='y' && axis!='z')
40600           throw CImgArgumentException(_cimg_instance
40601                                       "get_gradient(): Invalid specified axes '%s'.",
40602                                       cimg_instance,
40603                                       axes);
40604         const longT off = axis=='x'?1:axis=='y'?_width:_width*_height;
40605         if ((axis=='x' && _width==1) || (axis=='y' && _height==1) || (axis=='z' && _depth==1)) {
40606           grad.assign(_width,_height,_depth,_spectrum,0).move_to(res);
40607           continue;
40608         }
40609 
40610         const int _scheme = axis=='z' && (scheme==2 || scheme==3)?0:scheme;
40611         switch (_scheme) {
40612         case -1 : { // Backward finite differences
40613           grad.assign(_width,_height,_depth,_spectrum);
40614           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
40615           cimg_forXYZC(*this,x,y,z,c) {
40616             const ulongT pos = offset(x,y,z,c);
40617             if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
40618               grad[pos] = 0;
40619             else
40620               grad[pos] = (Tfloat)_data[pos] - _data[pos - off];
40621           }
40622           grad.move_to(res);
40623         } break;
40624         case 1 : { // Forward finite differences
40625           grad.assign(_width,_height,_depth,_spectrum);
40626           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
40627           cimg_forXYZC(*this,x,y,z,c) {
40628             const ulongT pos = offset(x,y,z,c);
40629             if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
40630               grad[pos] = 0;
40631             else
40632               grad[pos] = (Tfloat)_data[pos + off] - _data[pos];
40633           }
40634           grad.move_to(res);
40635         } break;
40636         case 2 : { // Sobel scheme
40637           grad.assign(_width,_height,_depth,_spectrum);
40638           if (axis=='x') // X-axis
40639             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40640                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
40641                                               _depth*_spectrum>=2))
40642             cimg_forZC(*this,z,c) {
40643               CImg_3x3(I,Tfloat);
40644               cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp + Inp - 2*Ipc + 2*Inc - Ipn + Inn;
40645             }
40646           else // Y-axis
40647             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40648                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
40649                                               _depth*_spectrum>=2))
40650             cimg_forZC(*this,z,c) {
40651               CImg_3x3(I,Tfloat);
40652               cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn;
40653             }
40654           grad.move_to(res);
40655         } break;
40656         case 3 : { // Rotation invariant scheme
40657           const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1));
40658           grad.assign(_width,_height,_depth,_spectrum);
40659           if (axis=='x') // X-axis
40660             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40661                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
40662                                               _depth*_spectrum>=2))
40663             cimg_forZC(*this,z,c) {
40664               CImg_3x3(I,Tfloat);
40665               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;
40666             }
40667           else // Y-axis
40668             cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40669                                cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
40670                                               _depth*_spectrum>=2))
40671             cimg_forZC(*this,z,c) {
40672               CImg_3x3(I,Tfloat);
40673               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;
40674             }
40675           grad.move_to(res);
40676         } break;
40677         case 4 : // Deriche filter
40678           get_deriche(0,1,axis).move_to(res);
40679           break;
40680         case 5 : // Van Vliet filter
40681           get_vanvliet(0,1,axis).move_to(res);
40682           break;
40683         default : { // Central finite differences
40684           grad.assign(_width,_height,_depth,_spectrum);
40685           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
40686           cimg_forXYZC(*this,x,y,z,c) {
40687             const ulongT pos = offset(x,y,z,c);
40688             if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
40689               grad[pos] = ((Tfloat)_data[pos + off] - _data[pos])/2;
40690             else if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
40691               grad[pos] = ((Tfloat)_data[pos] - _data[pos - off])/2;
40692             else
40693               grad[pos] = ((Tfloat)_data[pos + off] - _data[pos - off])/2;
40694           }
40695           grad.move_to(res);
40696         } break;
40697         }
40698       }
40699       return res;
40700     }
40701 
40702     //! Return image hessian.
40703     /**
40704        \param axes Axes considered for the hessian computation, as a C-string (e.g "xy").
40705     **/
40706     CImgList<Tfloat> get_hessian(const char *const axes=0) const {
40707       CImgList<Tfloat> res;
40708       char __axes[12] = { 0 };
40709       const char *_axes = axes?axes:__axes;
40710       if (!axes) {
40711         unsigned int k = 0;
40712         if (_width>1) { __axes[k++] = 'x'; __axes[k++] = 'x'; }
40713         if (_width>1 && _height>1) { __axes[k++] = 'x'; __axes[k++] = 'y'; }
40714         if (_width>1 && _depth>1) { __axes[k++] = 'x'; __axes[k++] = 'z'; }
40715         if (_height>1) { __axes[k++] = 'y'; __axes[k++] = 'y'; }
40716         if (_height>1 && _depth>1) { __axes[k++] = 'y'; __axes[k++] = 'z'; }
40717         if (_depth>1) { __axes[k++] = 'z'; __axes[k++] = 'z'; }
40718       }
40719       const unsigned int len = (unsigned int)std::strlen(_axes);
40720       if (len%2)
40721         throw CImgArgumentException(_cimg_instance
40722                                     "get_hessian(): Invalid specified axes '%s'.",
40723                                     cimg_instance,
40724                                     axes);
40725       CImg<Tfloat> hess;
40726       for (unsigned int k = 0; k<len; k+=2) {
40727         const char
40728           _axis1 = cimg::lowercase(_axes[k]),
40729           _axis2 = cimg::lowercase(_axes[k + 1]),
40730           axis1 = std::min(_axis1,_axis2),
40731           axis2 = std::max(_axis2,_axis2);
40732         if (axis1!='x' && axis1!='y' && axis1!='z' &&
40733             axis2!='x' && axis2!='y' && axis2!='z')
40734           throw CImgArgumentException(_cimg_instance
40735                                       "get_hessian(): Invalid specified axes '%s'.",
40736                                       cimg_instance,
40737                                       axes);
40738         const longT off = axis1=='x'?1:axis1=='y'?_width:_width*_height;
40739 
40740         hess.assign(_width,_height,_depth,_spectrum);
40741 
40742         if (((axis1=='x' || axis2=='x') && _width==1) ||
40743             ((axis1=='y' || axis2=='y') && _height==1) ||
40744             ((axis1=='z' || axis2=='z') && _depth==1)) {
40745           hess.fill(0).move_to(res);
40746           continue;
40747         }
40748 
40749         if (axis1==axis2) // Ixx, Iyy, Izz
40750           cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
40751           cimg_forXYZC(*this,x,y,z,c) {
40752             const ulongT pos = offset(x,y,z,c);
40753             if ((axis1=='x' && !x) || (axis1=='y' && !y) || (axis1=='z' && !z))
40754               hess[pos] = (Tfloat)_data[pos + off] - _data[pos];
40755             else if ((axis1=='x' && x==width() - 1) ||
40756                      (axis1=='y' && y==height() - 1) ||
40757                      (axis1=='z' && z==depth() - 1))
40758               hess[pos] = (Tfloat)_data[pos - off] - _data[pos];
40759             else
40760               hess[pos] = (Tfloat)_data[pos + off] + _data[pos - off] - 2*_data[pos];
40761           }
40762         else if (axis1=='x' && axis2=='y') // Ixy
40763           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
40764                              cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
40765                                             _depth*_spectrum>=2))
40766           cimg_forZC(*this,z,c) {
40767             CImg_3x3(I,Tfloat);
40768             cimg_for3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Inn + Ipp - Inp - Ipn)/4;
40769           }
40770         else if (axis1=='x' && axis2=='z') // Ixz
40771           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
40772                                                          _spectrum>=2))
40773           cimg_forC(*this,c) {
40774             CImg_3x3x3(I,Tfloat);
40775             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Incn + Ipcp - Incp - Ipcn)/4;
40776           }
40777         else // Iyz
40778           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
40779                                                          _spectrum>=2))
40780           cimg_forC(*this,c) {
40781             CImg_3x3x3(I,Tfloat);
40782             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Icnn + Icpp - Icnp - Icpn)/4;
40783           }
40784         hess.move_to(res);
40785       }
40786       return res;
40787     }
40788 
40789     //! Compute image Laplacian.
40790     CImg<T>& laplacian() {
40791       return get_laplacian().move_to(*this);
40792     }
40793 
40794     //! Compute image Laplacian \newinstance.
40795     CImg<Tfloat> get_laplacian() const {
40796       if (is_empty()) return CImg<Tfloat>();
40797       CImg<Tfloat> res(_width,_height,_depth,_spectrum);
40798       if (_depth>1) { // 3D
40799         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
40800                                                        _spectrum>=2))
40801         cimg_forC(*this,c) {
40802           Tfloat *ptrd = res.data(0,0,0,c);
40803           CImg_3x3x3(I,Tfloat);
40804           cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
40805         }
40806       } else if (_height>1) { // 2D
40807         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
40808                                                        _depth*_spectrum>=2))
40809         cimg_forC(*this,c) {
40810           Tfloat *ptrd = res.data(0,0,0,c);
40811           CImg_3x3(I,Tfloat);
40812           cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
40813         }
40814       } else { // 1D
40815         cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 &&
40816                                                        _height*_depth*_spectrum>=2))
40817         cimg_forC(*this,c) {
40818           Tfloat *ptrd = res.data(0,0,0,c);
40819           CImg_3x3(I,Tfloat);
40820           cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc;
40821         }
40822       }
40823       return res;
40824     }
40825 
40826     //! Compute the structure tensor field of an image.
40827     /**
40828        \param is_fwbw_scheme scheme. Can be <tt>{ false=centered | true=forward-backward }</tt>
40829     **/
40830     CImg<T>& structure_tensors(const bool is_fwbw_scheme=false) {
40831       return get_structure_tensors(is_fwbw_scheme).move_to(*this);
40832     }
40833 
40834     //! Compute the structure tensor field of an image \newinstance.
40835     CImg<Tfloat> get_structure_tensors(const bool is_fwbw_scheme=false) const {
40836       if (is_empty()) return *this;
40837       CImg<Tfloat> res;
40838       if (_depth>1) { // 3D
40839         res.assign(_width,_height,_depth,6,0);
40840         if (!is_fwbw_scheme) { // Classical central finite differences
40841           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
40842                                                          _spectrum>=2))
40843           cimg_forC(*this,c) {
40844             Tfloat
40845               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
40846               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
40847             CImg_3x3x3(I,Tfloat);
40848             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
40849               const Tfloat
40850                 ix = (Incc - Ipcc)/2,
40851                 iy = (Icnc - Icpc)/2,
40852                 iz = (Iccn - Iccp)/2;
40853               *(ptrd0++)+=ix*ix;
40854               *(ptrd1++)+=ix*iy;
40855               *(ptrd2++)+=ix*iz;
40856               *(ptrd3++)+=iy*iy;
40857               *(ptrd4++)+=iy*iz;
40858               *(ptrd5++)+=iz*iz;
40859             }
40860           }
40861         } else { // Forward/backward finite differences
40862           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
40863                                                          _spectrum>=2))
40864           cimg_forC(*this,c) {
40865             Tfloat
40866               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
40867               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
40868             CImg_3x3x3(I,Tfloat);
40869             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
40870               const Tfloat
40871                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
40872                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
40873                 izf = Iccn - Iccc, izb = Iccc - Iccp;
40874               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
40875               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
40876               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
40877               *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
40878               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
40879               *(ptrd5++)+=(izf*izf + izb*izb)/2;
40880             }
40881           }
40882         }
40883       } else { // 2D
40884         res.assign(_width,_height,_depth,3,0);
40885         if (!is_fwbw_scheme) { // Classical central finite differences
40886           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
40887                                                          _depth*_spectrum>=2))
40888           cimg_forC(*this,c) {
40889             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
40890             CImg_3x3(I,Tfloat);
40891             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
40892               const Tfloat
40893                 ix = (Inc - Ipc)/2,
40894                 iy = (Icn - Icp)/2;
40895               *(ptrd0++)+=ix*ix;
40896               *(ptrd1++)+=ix*iy;
40897               *(ptrd2++)+=iy*iy;
40898             }
40899           }
40900         } else { // Forward/backward finite differences (version 2)
40901           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
40902                                                          _depth*_spectrum>=2))
40903           cimg_forC(*this,c) {
40904             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
40905             CImg_3x3(I,Tfloat);
40906             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
40907               const Tfloat
40908                 ixf = Inc - Icc, ixb = Icc - Ipc,
40909                 iyf = Icn - Icc, iyb = Icc - Icp;
40910               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
40911               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
40912               *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
40913             }
40914           }
40915         }
40916       }
40917       return res;
40918     }
40919 
40920     //! Compute field of diffusion tensors for edge-preserving smoothing.
40921     /**
40922        \param sharpness Sharpness
40923        \param anisotropy Anisotropy
40924        \param alpha Standard deviation of the gradient blur.
40925        \param sigma Standard deviation of the structure tensor blur.
40926        \param is_sqrt Tells if the square root of the tensor field is computed instead.
40927     **/
40928     CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
40929                                const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
40930       CImg<Tfloat> res;
40931       const float
40932         nsharpness = std::max(sharpness,1e-5f),
40933         power1 = (is_sqrt?0.5f:1)*nsharpness,
40934         power2 = power1/(1e-7f + 1 - anisotropy);
40935       blur(alpha).normalize(0,(T)255);
40936 
40937       if (_depth>1) { // 3D
40938         get_structure_tensors().move_to(res).blur(sigma);
40939         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40940                                                                    _height*_depth>=(cimg_openmp_sizefactor)*256))
40941         cimg_forYZ(*this,y,z) {
40942           Tfloat
40943             *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2),
40944             *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5);
40945           CImg<floatT> val(3), vec(3,3);
40946           cimg_forX(*this,x) {
40947             res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
40948             const float
40949               _l1 = val[2], _l2 = val[1], _l3 = val[0],
40950               l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
40951               ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
40952               vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
40953               wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
40954               n1 = (float)std::pow(1 + l1 + l2 + l3,-power1),
40955               n2 = (float)std::pow(1 + l1 + l2 + l3,-power2);
40956             *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
40957             *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
40958             *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
40959             *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
40960             *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
40961             *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
40962           }
40963         }
40964       } else { // for 2D images
40965         get_structure_tensors().move_to(res).blur(sigma);
40966         cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
40967                                                        _height>=(cimg_openmp_sizefactor)*256))
40968         cimg_forY(*this,y) {
40969           Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2);
40970           CImg<floatT> val(2), vec(2,2);
40971           cimg_forX(*this,x) {
40972             res.get_tensor_at(x,y).symmetric_eigen(val,vec);
40973             const float
40974               _l1 = val[1], _l2 = val[0],
40975               l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
40976               ux = vec(1,0), uy = vec(1,1),
40977               vx = vec(0,0), vy = vec(0,1),
40978               n1 = (float)std::pow(1 + l1 + l2,-power1),
40979               n2 = (float)std::pow(1 + l1 + l2,-power2);
40980             *(ptrd0++) = n1*ux*ux + n2*vx*vx;
40981             *(ptrd1++) = n1*ux*uy + n2*vx*vy;
40982             *(ptrd2++) = n1*uy*uy + n2*vy*vy;
40983           }
40984         }
40985       }
40986       return res.move_to(*this);
40987     }
40988 
40989     //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance.
40990     CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
40991                                        const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
40992       return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
40993     }
40994 
40995     //! Estimate displacement field between two images.
40996     /**
40997        \param source Reference image.
40998        \param smoothness Smoothness of estimated displacement field.
40999        \param precision Precision required for algorithm convergence.
41000        \param nb_scales Number of scales used to estimate the displacement field.
41001        \param iteration_max Maximum number of iterations allowed for one scale.
41002        \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)).
41003        \param guide Image used as the initial correspondence estimate for the algorithm.
41004        'guide' may have a last channel with boolean values (0=false | other=true) that
41005        tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
41006     **/
41007     CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.f,
41008                           const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
41009                           const bool is_backward=false,
41010                           const CImg<floatT>& guide=CImg<floatT>::const_empty()) {
41011       return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide).
41012         move_to(*this);
41013     }
41014 
41015     //! Estimate displacement field between two images \newinstance.
41016     CImg<floatT> get_displacement(const CImg<T>& source,
41017                                   const float smoothness=0.1f, const float precision=5.f,
41018                                   const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
41019                                   const bool is_backward=false,
41020                                   const CImg<floatT>& guide=CImg<floatT>::const_empty()) const {
41021       if (is_empty() || !source) return +*this;
41022       if (!is_sameXYZC(source))
41023         throw CImgArgumentException(_cimg_instance
41024                                     "displacement(): Instance and source image (%u,%u,%u,%u,%p) have "
41025                                     "different dimensions.",
41026                                     cimg_instance,
41027                                     source._width,source._height,source._depth,source._spectrum,source._data);
41028       if (precision<0)
41029         throw CImgArgumentException(_cimg_instance
41030                                     "displacement(): Invalid specified precision %g "
41031                                     "(should be >=0)",
41032                                     cimg_instance,
41033                                     precision);
41034 
41035       const bool is_3d = source._depth>1;
41036       const unsigned int constraint = is_3d?3:2;
41037 
41038       if (guide &&
41039           (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<constraint))
41040         throw CImgArgumentException(_cimg_instance
41041                                     "displacement(): Specified guide (%u,%u,%u,%u,%p) "
41042                                     "has invalid dimensions.",
41043                                     cimg_instance,
41044                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
41045 
41046       const unsigned int
41047         mins = is_3d?cimg::min(_width,_height,_depth):std::min(_width,_height),
41048         _nb_scales = nb_scales>0?nb_scales:
41049         (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1);
41050 
41051       const float _precision = (float)std::pow(10.,-(double)precision);
41052       float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
41053       const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
41054 
41055       CImg<floatT> U, V;
41056       floatT bound = 0;
41057       for (int scale = (int)_nb_scales - 1; scale>=0; --scale) {
41058         const float factor = (float)std::pow(1.5,(double)scale);
41059         const unsigned int
41060           _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
41061           _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
41062           _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
41063         if (sw<5 && sh<5 && (!is_3d || sd<5)) continue;  // Skip too small scales
41064         const CImg<Tfloat>
41065           I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
41066           I2 = (get_resize(I1,2)-=tm)/=tdelta;
41067         if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V);
41068         if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
41069         else {
41070           if (guide)
41071             guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U);
41072           else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
41073         }
41074 
41075         float dt = 2, energy = cimg::type<float>::max();
41076         const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
41077         cimg_abort_init;
41078 
41079         for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
41080           cimg_abort_test;
41081           float _energy = 0;
41082 
41083           if (is_3d) { // 3D version
41084             if (smoothness>=0) // Isotropic regularization
41085               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41086                                  cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
41087                                                 _width>=(cimg_openmp_sizefactor)*16)
41088                                  reduction(+:_energy))
41089               cimg_forYZ(U,y,z) {
41090                 const int
41091                   _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
41092                   _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
41093                 cimg_for3X(U,x) {
41094                   const float
41095                     X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
41096                     Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
41097                     Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
41098                   float delta_I = 0, _energy_regul = 0;
41099                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
41100                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
41101                   cimg_forC(U,c) {
41102                     const float
41103                       Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
41104                       Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
41105                       Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
41106                       Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
41107                       Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
41108                       Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
41109                     U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
41110                                                           smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt);
41111                     _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
41112                   }
41113                   if (is_backward) { // Constraint displacement vectors to stay in image
41114                     if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
41115                     if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
41116                     if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
41117                     bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
41118                     bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
41119                     bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
41120                   } else {
41121                     if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
41122                     if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
41123                     if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
41124                     bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
41125                     bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
41126                     bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
41127                   }
41128                   _energy+=delta_I*delta_I + smoothness*_energy_regul;
41129                 }
41130                 if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
41131                     U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
41132                     U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
41133                     U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
41134                   }
41135               } else { // Anisotropic regularization
41136               const float nsmoothness = -smoothness;
41137               cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41138                                  cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
41139                                                 _width>=(cimg_openmp_sizefactor)*16)
41140                                  reduction(+:_energy))
41141               cimg_forYZ(U,y,z) {
41142                 const int
41143                   _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
41144                   _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
41145                 cimg_for3X(U,x) {
41146                   const float
41147                     X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
41148                     Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
41149                     Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
41150                   float delta_I = 0, _energy_regul = 0;
41151                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
41152                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
41153                   cimg_forC(U,c) {
41154                     const float
41155                       Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
41156                       Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
41157                       Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
41158                       N2 = Ux*Ux + Uy*Uy + Uz*Uz,
41159                       N = std::sqrt(N2),
41160                       N3 = 1e-5f + N2*N,
41161                       coef_a = (1 - Ux*Ux/N2)/N,
41162                       coef_b = -2*Ux*Uy/N3,
41163                       coef_c = -2*Ux*Uz/N3,
41164                       coef_d = (1 - Uy*Uy/N2)/N,
41165                       coef_e = -2*Uy*Uz/N3,
41166                       coef_f = (1 - Uz*Uz/N2)/N,
41167                       Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
41168                       Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
41169                       Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
41170                       Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
41171                       Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
41172                       Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
41173                     U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
41174                                                           nsmoothness* ( coef_a*Uxx + coef_b*Uxy +
41175                                                                          coef_c*Uxz + coef_d*Uyy +
41176                                                                          coef_e*Uyz + coef_f*Uzz ))
41177                                          )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt);
41178                     _energy_regul+=N;
41179                   }
41180                   if (is_backward) { // Constraint displacement vectors to stay in image
41181                     if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
41182                     if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
41183                     if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
41184                     bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
41185                     bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
41186                     bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
41187                   } else {
41188                     if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
41189                     if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
41190                     if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
41191                     bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
41192                     bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
41193                     bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
41194                   }
41195                   _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
41196                 }
41197                 if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
41198                     U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
41199                     U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
41200                     U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
41201                   }
41202               }
41203             }
41204           } else { // 2D version
41205             if (smoothness>=0) // Isotropic regularization
41206               cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
41207                                                              _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
41208               cimg_forY(U,y) {
41209                 const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
41210                 cimg_for3X(U,x) {
41211                   const float
41212                     X = is_backward?x - U(x,y,0):x + U(x,y,0),
41213                     Y = is_backward?y - U(x,y,1):y + U(x,y,1);
41214                   float delta_I = 0, _energy_regul = 0;
41215                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
41216                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
41217                   cimg_forC(U,c) {
41218                     const float
41219                       Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
41220                       Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
41221                       Uxx = U(_n1x,y,c) + U(_p1x,y,c),
41222                       Uyy = U(x,_n1y,c) + U(x,_p1y,c);
41223                     U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
41224                                                       smoothness*( Uxx + Uyy )))/(1 + 4*smoothness*dt);
41225                     _energy_regul+=Ux*Ux + Uy*Uy;
41226                   }
41227                   if (is_backward) { // Constraint displacement vectors to stay in image
41228                     if (U(x,y,0)>x) U(x,y,0) = (float)x;
41229                     if (U(x,y,1)>y) U(x,y,1) = (float)y;
41230                     bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
41231                     bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
41232                   } else {
41233                     if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
41234                     if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
41235                     bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
41236                     bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
41237                   }
41238                   _energy+=delta_I*delta_I + smoothness*_energy_regul;
41239                 }
41240                 if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
41241                     U(_x,_y,0) = V(_x,_y,0)/factor;
41242                     U(_x,_y,1) = V(_x,_y,1)/factor;
41243                   }
41244               } else { // Anisotropic regularization
41245               const float nsmoothness = -smoothness;
41246               cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
41247                                                              _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
41248               cimg_forY(U,y) {
41249                 const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
41250                 cimg_for3X(U,x) {
41251                   const float
41252                     X = is_backward?x - U(x,y,0):x + U(x,y,0),
41253                     Y = is_backward?y - U(x,y,1):y + U(x,y,1);
41254                   float delta_I = 0, _energy_regul = 0;
41255                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
41256                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
41257                   cimg_forC(U,c) {
41258                     const float
41259                       Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
41260                       Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
41261                       N2 = Ux*Ux + Uy*Uy,
41262                       N = std::sqrt(N2),
41263                       N3 = 1e-5f + N2*N,
41264                       coef_a = Uy*Uy/N3,
41265                       coef_b = -2*Ux*Uy/N3,
41266                       coef_c = Ux*Ux/N3,
41267                       Uxx = U(_n1x,y,c) + U(_p1x,y,c),
41268                       Uyy = U(x,_n1y,c) + U(x,_p1y,c),
41269                       Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
41270                     U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
41271                                                       nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/
41272                       (1 + 2*(coef_a + coef_c)*nsmoothness*dt);
41273                     _energy_regul+=N;
41274                   }
41275                   if (is_backward) { // Constraint displacement vectors to stay in image
41276                     if (U(x,y,0)>x) U(x,y,0) = (float)x;
41277                     if (U(x,y,1)>y) U(x,y,1) = (float)y;
41278                     bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
41279                     bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
41280                   } else {
41281                     if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
41282                     if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
41283                     bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
41284                     bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
41285                   }
41286                   _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
41287                 }
41288                 if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
41289                     U(_x,_y,0) = V(_x,_y,0)/factor;
41290                     U(_x,_y,1) = V(_x,_y,1)/factor;
41291                   }
41292               }
41293             }
41294           }
41295           const float d_energy = (_energy - energy)/(sw*sh*sd);
41296           if (d_energy<=0 && -d_energy<_precision) break;
41297           if (d_energy>0) dt*=0.5f;
41298           energy = _energy;
41299         }
41300       }
41301       return U;
41302     }
41303 
41304     //! Compute correspondence map between two images, using a patch-matching algorithm.
41305     /**
41306         \param patch_image The image containing the reference patches to match with the instance image.
41307         \param patch_width Width of the patch used for matching.
41308         \param patch_height Height of the patch used for matching.
41309         \param patch_depth Depth of the patch used for matching.
41310         \param nb_iterations Number of patch-match iterations.
41311         \param nb_randoms Number of randomization attempts (per pixel).
41312         \param patch_penalization Penalization factor in score related patch occurrences.
41313                if negative, also tells that identity result is not avoided.
41314         \param guide Image used as the initial correspondence estimate for the algorithm.
41315           'guide' may have a last channel with boolean values (0=false | other=true) that
41316           tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
41317         \param[out] matching_score Returned as the image of matching scores.
41318     **/
41319     template<typename t1, typename t2>
41320     CImg<T>& matchpatch(const CImg<T>& patch_image,
41321                         const unsigned int patch_width,
41322                         const unsigned int patch_height,
41323                         const unsigned int patch_depth,
41324                         const unsigned int nb_iterations,
41325                         const unsigned int nb_randoms,
41326                         const float patch_penalization,
41327                         const CImg<t1> &guide,
41328                         CImg<t2> &matching_score) {
41329       return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
41330                             nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this);
41331     }
41332 
41333     //! Compute correspondence map between two images, using the patch-match algorithm \newinstance.
41334     template<typename t1, typename t2>
41335     CImg<intT> get_matchpatch(const CImg<T>& patch_image,
41336                               const unsigned int patch_width,
41337                               const unsigned int patch_height,
41338                               const unsigned int patch_depth,
41339                               const unsigned int nb_iterations,
41340                               const unsigned int nb_randoms,
41341                               const float patch_penalization,
41342                               const CImg<t1> &guide,
41343                               CImg<t2> &matching_score) const {
41344       return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
41345                          nb_iterations,nb_randoms,patch_penalization,
41346                          guide,true,matching_score);
41347     }
41348 
41349     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
41350     template<typename t>
41351     CImg<T>& matchpatch(const CImg<T>& patch_image,
41352                         const unsigned int patch_width,
41353                         const unsigned int patch_height,
41354                         const unsigned int patch_depth,
41355                         const unsigned int nb_iterations=5,
41356                         const unsigned int nb_randoms=5,
41357                         const float patch_penalization=0,
41358                         const CImg<t> &guide=CImg<t>::const_empty()) {
41359       return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
41360                             nb_iterations,nb_randoms,patch_penalization,guide).move_to(*this);
41361     }
41362 
41363     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
41364     template<typename t>
41365     CImg<intT> get_matchpatch(const CImg<T>& patch_image,
41366                               const unsigned int patch_width,
41367                               const unsigned int patch_height,
41368                               const unsigned int patch_depth,
41369                               const unsigned int nb_iterations=5,
41370                               const unsigned int nb_randoms=5,
41371                               const float patch_penalization=0,
41372                               const CImg<t> &guide=CImg<t>::const_empty()) const {
41373       CImg<T> matching_score;
41374       return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
41375                          nb_iterations,nb_randoms,patch_penalization,guide,false,matching_score);
41376     }
41377 
41378     template<typename t1, typename t2>
41379     CImg<intT> _matchpatch(const CImg<T>& patch_image,
41380                            const unsigned int patch_width,
41381                            const unsigned int patch_height,
41382                            const unsigned int patch_depth,
41383                            const unsigned int nb_iterations,
41384                            const unsigned int nb_randoms,
41385                            const float patch_penalization,
41386                            const CImg<t1> &guide,
41387                            const bool is_matching_score,
41388                            CImg<t2> &matching_score) const {
41389       if (is_empty()) return CImg<intT>::const_empty();
41390       if (patch_image._spectrum!=_spectrum)
41391         throw CImgArgumentException(_cimg_instance
41392                                     "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) "
41393                                     "have different spectrums.",
41394                                     cimg_instance,
41395                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
41396                                     patch_image._data);
41397       if (patch_width>_width || patch_height>_height || patch_depth>_depth)
41398         throw CImgArgumentException(_cimg_instance
41399                                     "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
41400                                     "of the instance image.",
41401                                     cimg_instance,patch_width,patch_height,patch_depth);
41402       if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth)
41403         throw CImgArgumentException(_cimg_instance
41404                                     "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
41405                                     "of the patch image image (%u,%u,%u,%u,%p).",
41406                                     cimg_instance,patch_width,patch_height,patch_depth,
41407                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
41408                                     patch_image._data);
41409       const unsigned int
41410         _constraint = patch_image._depth>1?3:2,
41411         constraint = guide._spectrum>_constraint?_constraint:0;
41412 
41413       if (guide &&
41414           (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint))
41415         throw CImgArgumentException(_cimg_instance
41416                                     "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions "
41417                                     "considering instance and patch image (%u,%u,%u,%u,%p).",
41418                                     cimg_instance,
41419                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data,
41420                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
41421                                     patch_image._data);
41422 
41423       CImg<intT> a_map(_width,_height,_depth,patch_image._depth>1?3:2);
41424       CImg<ucharT> is_updated(_width,_height,_depth,1,3);
41425       CImg<floatT> score(_width,_height,_depth);
41426       CImg<uintT> occ;
41427       const float _patch_penalization = cimg::abs(patch_penalization);
41428       const bool allow_identity = patch_penalization>=0;
41429       if (_patch_penalization!=0) occ.assign(patch_image._width,patch_image._height,patch_image._depth,1,0);
41430       const int
41431         psizew = (int)patch_width,  psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1,
41432         psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1,
41433         psized = (int)patch_depth,  psized1 = psized/2, psized2 = psized - psized1 - 1;
41434 
41435       // Interleave image buffers to speed up patch comparison (cache-friendly).
41436       CImg<T> in_this = get_permute_axes("cxyz");
41437       in_this._width = _width*_spectrum;
41438       in_this._height = _height;
41439       in_this._depth = _depth;
41440       in_this._spectrum = 1;
41441       CImg<T> in_patch = patch_image.get_permute_axes("cxyz");
41442       in_patch._width = patch_image._width*patch_image._spectrum;
41443       in_patch._height = patch_image._height;
41444       in_patch._depth = patch_image._depth;
41445       in_patch._spectrum = 1;
41446 
41447       if (_depth>1 || patch_image._depth>1) { // 3D version
41448 
41449         // Initialize correspondence map.
41450         if (guide)
41451           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_width,64))
41452             cimg_forXYZ(*this,x,y,z) { // User-defined initialization
41453             const int
41454               cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
41455               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
41456               cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
41457               u = cimg::cut((int)guide(x,y,z,0),cx1,patch_image.width() - 1 - cx2),
41458               v = cimg::cut((int)guide(x,y,z,1),cy1,patch_image.height() - 1 - cy2),
41459               w = cimg::cut((int)guide(x,y,z,2),cz1,patch_image.depth() - 1 - cz2);
41460             a_map(x,y,z,0) = u;
41461             a_map(x,y,z,1) = v;
41462             a_map(x,y,z,2) = w;
41463             score(x,y,z) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41464                                        x - cx1,y - cy1,z - cz1,
41465                                        u - cx1,v - cy1,w - cz1,
41466                                        u,v,w,0,allow_identity,cimg::type<float>::inf());
41467           } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
41468             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
41469 #if cimg_use_openmp!=0
41470             rng+=omp_get_thread_num();
41471 #endif
41472             cimg_pragma_openmp(for cimg_openmp_collapse(2))
41473               cimg_forXYZ(*this,x,y,z) { // Random initialization
41474               const int
41475                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
41476                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
41477                 cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
41478                 u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
41479                 v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng)),
41480                 w = (int)cimg::round(cimg::rand(cz1,patch_image.depth() - 1 - cz2,&rng));
41481               a_map(x,y,z,0) = u;
41482               a_map(x,y,z,1) = v;
41483               a_map(x,y,z,2) = w;
41484               score(x,y,z) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41485                                          x - cx1,y - cy1,z - cz1,
41486                                          u - cx1,v - cy1,w - cz1,
41487                                          u,v,w,0,allow_identity,cimg::type<float>::inf());
41488             }
41489             cimg::srand(rng);
41490           }
41491 
41492         // Start iteration loop.
41493         cimg_abort_init;
41494         for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
41495           cimg_abort_test;
41496           const bool is_backward = iter&1;
41497           const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
41498 
41499           cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
41500                                                      iter<nb_iterations-2)) {
41501             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
41502 
41503 #if cimg_use_openmp!=0
41504             rng+=omp_get_thread_num();
41505 #endif
41506             cimg_pragma_openmp(for cimg_openmp_collapse(2))
41507               cimg_forXYZ(*this,X,Y,Z) {
41508               const int
41509                 x = is_backward?width() - 1 - X:X,
41510                 y = is_backward?height() - 1 - Y:Y,
41511                 z = is_backward?depth() - 1 - Z:Z;
41512               if (score(x,y,z)<=1e-5 || (constraint && guide(x,y,z,constraint)!=0)) continue;
41513               const int
41514                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
41515                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
41516                 cz1 = z<=psized1?z:(z<depth()  - psized2?psized1:psized + z - depth()),  cz2 = psized - cz1 - 1,
41517                 xp = x - cx1,
41518                 yp = y - cy1,
41519                 zp = z - cz1;
41520 
41521               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;
41522               const float best_score0 = score(x,y,z);
41523               float best_score = best_score0, s;
41524 
41525               if (x>0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor
41526                 u = a_map(x - 1,y,z,0);
41527                 v = a_map(x - 1,y,z,1);
41528                 w = a_map(x - 1,y,z,2);
41529                 if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
41530                     v>=cy1 && v<patch_image.height() - cy2 &&
41531                     w>=cz1 && w<patch_image.depth() - cz2) {
41532                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41533                                   xp,yp,zp,u + 1 - cx1,v - cy1,w - cz1,
41534                                   u,v,w,_patch_penalization,allow_identity,best_score);
41535                   if (s<best_score) { best_u = u + 1; best_v = v; best_w = w; best_score = s; }
41536                 }
41537               }
41538               if (y>0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor
41539                 u = a_map(x,y - 1,z,0);
41540                 v = a_map(x,y - 1,z,1);
41541                 w = a_map(x,y - 1,z,2);
41542                 if (u>=cx1 && u<patch_image.width() - cx2 &&
41543                     v>=cy1 - 1 && v<patch_image.height() - 1 - cy2 &&
41544                     w>=cz1 && w<patch_image.depth() - cz2) {
41545                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41546                                   xp,yp,zp,u - cx1,v + 1 - cy1,w - cz1,
41547                                   u,v,w,_patch_penalization,allow_identity,best_score);
41548                   if (s<best_score) { best_u = u; best_v = v + 1; best_w = w; best_score = s; }
41549                 }
41550               }
41551               if (z>0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor
41552                 u = a_map(x,y,z - 1,0);
41553                 v = a_map(x,y,z - 1,1);
41554                 w = a_map(x,y,z - 1,2);
41555                 if (u>=cx1 && u<patch_image.width() - cx2 &&
41556                     v>=cy1 && v<patch_image.height() - cy2 &&
41557                     w>=cz1 - 1 && w<patch_image.depth() - 1 - cz2) {
41558                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41559                                   xp,yp,zp,u - cx1,v - cy1,w + 1 - cz1,
41560                                   u,v,w,_patch_penalization,allow_identity,best_score);
41561                   if (s<best_score) { best_u = u; best_v = v; best_w = w + 1; best_score = s; }
41562                 }
41563               }
41564               if (x<width() - 1 && (is_updated(x + 1,y,z)&cmask)) { // Compare with right neighbor
41565                 u = a_map(x + 1,y,z,0);
41566                 v = a_map(x + 1,y,z,1);
41567                 w = a_map(x + 1,y,z,2);
41568                 if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
41569                     v>=cy1 && v<patch_image.height() - cy2 &&
41570                     w>=cz1 && w<patch_image.depth() - cz2) {
41571                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41572                                   xp,yp,zp,u - 1 - cx1,v - cy1,w - cz1,
41573                                   u,v,w,_patch_penalization,allow_identity,best_score);
41574                   if (s<best_score) { best_u = u - 1; best_v = v; best_w = w; best_score = s; }
41575                 }
41576               }
41577               if (y<height() - 1 && (is_updated(x,y + 1,z)&cmask)) { // Compare with bottom neighbor
41578                 u = a_map(x,y + 1,z,0);
41579                 v = a_map(x,y + 1,z,1);
41580                 w = a_map(x,y + 1,z,2);
41581                 if (u>=cx1 && u<patch_image.width() - cx2 &&
41582                     v>=cy1 + 1 && v<patch_image.height() + 1 - cy2 &&
41583                     w>=cz1 && w<patch_image.depth() - cz2) {
41584                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41585                                   xp,yp,zp,u - cx1,v - 1 - cy1,w - cz1,
41586                                   u,v,w,_patch_penalization,allow_identity,best_score);
41587                   if (s<best_score) { best_u = u; best_v = v - 1; best_w = w; best_score = s; }
41588                 }
41589               }
41590               if (z<depth() - 1 && (is_updated(x,y,z + 1)&cmask)) { // Compare with forward neighbor
41591                 u = a_map(x,y,z + 1,0);
41592                 v = a_map(x,y,z + 1,1);
41593                 w = a_map(x,y,z + 1,2);
41594                 if (u>=cx1 && u<patch_image.width() - cx2 &&
41595                     v>=cy1 && v<patch_image.height() - cy2 &&
41596                     w>=cz1 + 1 && w<patch_image.depth() + 1 - cz2) {
41597                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41598                                   xp,yp,zp,u - cx1,v - cy1,w - 1 - cz1,
41599                                   u,v,w,_patch_penalization,allow_identity,best_score);
41600                   if (s<best_score) { best_u = u; best_v = v; best_w = w - 1; best_score = s; }
41601                 }
41602               }
41603 
41604               float
41605                 dw = (float)patch_image.width(),
41606                 dh = (float)patch_image.height(),
41607                 dd = (float)patch_image.depth();
41608               for (unsigned int i = 0; i<nb_randoms; ++i) {
41609                 u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
41610                                                 std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
41611                 v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
41612                                                 std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
41613                 w = (int)cimg::round(cimg::rand(std::max((float)cz1,best_w - dd),
41614                                                 std::min(patch_image.depth() - 1.f - cz2,best_w + dd),&rng));
41615                 if (u!=best_u || v!=best_v || w!=best_w) {
41616                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,patch_depth,_spectrum,
41617                                   xp,yp,zp,u - cx1,v - cy1,w - cz1,
41618                                   u,v,w,_patch_penalization,allow_identity,best_score);
41619                   if (s<best_score) { best_u = u; best_v = v; best_w = w; best_score = s; }
41620                   dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f); dd = std::max(5.f,dd*0.5f);
41621                 }
41622               }
41623 
41624               if (best_score<best_score0) {
41625                 if (_patch_penalization!=0) {
41626                   uintT &n_occ = occ(a_map(x,y,z,0),a_map(x,y,z,1),a_map(x,y,z,2));
41627                   if (n_occ) cimg_pragma_openmp(atomic) --n_occ;
41628                 }
41629                 a_map(x,y,z,0) = best_u;
41630                 a_map(x,y,z,1) = best_v;
41631                 a_map(x,y,z,2) = best_w;
41632                 score(x,y,z) = best_score;
41633                 is_updated(x,y,z) = 3;
41634               } else is_updated(x,y,z)&=~nmask;
41635               if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++occ(best_u,best_v,best_w);
41636             }
41637             cimg::srand(rng);
41638           }
41639         }
41640 
41641       } else { // 2D version
41642 
41643         // Initialize correspondence map.
41644         if (guide)
41645           cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,64))
41646             cimg_forXY(*this,x,y) { // User-defined initialization
41647             const int
41648               cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
41649               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
41650               u = cimg::cut((int)guide(x,y,0),cx1,patch_image.width() - 1 - cx2),
41651               v = cimg::cut((int)guide(x,y,1),cy1,patch_image.height() - 1 - cy2);
41652             a_map(x,y,0) = u;
41653             a_map(x,y,1) = v;
41654             score(x,y) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
41655                                      x - cx1,y - cy1,u - cx1,v - cy1,
41656                                      u,v,0,allow_identity,cimg::type<float>::inf());
41657           } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
41658             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
41659 
41660 #if cimg_use_openmp!=0
41661             rng+=omp_get_thread_num();
41662 #endif
41663             cimg_pragma_openmp(for)
41664               cimg_forXY(*this,x,y) { // Random initialization
41665               const int
41666                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
41667                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
41668                 u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
41669                 v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng));
41670               a_map(x,y,0) = u;
41671               a_map(x,y,1) = v;
41672               score(x,y) = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
41673                                        x - cx1,y - cy1,u - cx1,v - cy1,
41674                                        u,v,0,allow_identity,cimg::type<float>::inf());
41675             }
41676             cimg::srand(rng);
41677           }
41678 
41679         // Start iteration loop.
41680         cimg_abort_init;
41681         for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
41682           cimg_abort_test;
41683           const bool is_backward = iter&1;
41684           const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
41685 
41686           cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
41687                                                      iter<nb_iterations-2)) {
41688             cimg_uint64 rng = (cimg::_rand(),cimg::rng());
41689 
41690 #if cimg_use_openmp!=0
41691             rng+=omp_get_thread_num();
41692 #endif
41693             cimg_pragma_openmp(for)
41694               cimg_forXY(*this,X,Y) {
41695               const int
41696                 x = is_backward?width() - 1 - X:X,
41697                 y = is_backward?height() - 1 - Y:Y;
41698               if (score(x,y)<=1e-5 || (constraint && guide(x,y,constraint)!=0)) continue;
41699               const int
41700                 cx1 = x<=psizew1?x:(x<width()  - psizew2?psizew1:psizew + x - width()),  cx2 = psizew - cx1 - 1,
41701                 cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
41702                 xp = x - cx1,
41703                 yp = y - cy1;
41704 
41705               int best_u = a_map(x,y,0), best_v = a_map(x,y,1), u, v;
41706               const float best_score0 = score(x,y);
41707               float best_score = best_score0, s;
41708 
41709               if (x>0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor
41710                 u = a_map(x - 1,y,0);
41711                 v = a_map(x - 1,y,1);
41712                 if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
41713                     v>=cy1 && v<patch_image.height() - cy2) {
41714                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
41715                                   xp,yp,u + 1 - cx1,v - cy1,
41716                                   u,v,_patch_penalization,allow_identity,best_score);
41717                   if (s<best_score) { best_u = u + 1; best_v = v; best_score = s; }
41718                 }
41719               }
41720               if (y>0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor
41721                 u = a_map(x,y - 1,0);
41722                 v = a_map(x,y - 1,1);
41723                 if (u>=cx1 && u<patch_image.width() - cx2 &&
41724                     v>=cy1 - 1 && v<patch_image.height() - 1 - cy2) {
41725                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
41726                                   xp,yp,u - cx1,v + 1 - cy1,
41727                                   u,v,_patch_penalization,allow_identity,best_score);
41728                   if (s<best_score) { best_u = u; best_v = v + 1; best_score = s; }
41729                 }
41730               }
41731               if (x<width() - 1 && (is_updated(x + 1,y)&cmask)) { // Compare with right neighbor
41732                 u = a_map(x + 1,y,0);
41733                 v = a_map(x + 1,y,1);
41734                 if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
41735                     v>=cy1 && v<patch_image.height() - cy2) {
41736                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
41737                                   xp,yp,u - 1 - cx1,v - cy1,
41738                                   u,v,_patch_penalization,allow_identity,best_score);
41739                   if (s<best_score) { best_u = u - 1; best_v = v; best_score = s; }
41740                 }
41741               }
41742               if (y<height() - 1 && (is_updated(x,y + 1)&cmask)) { // Compare with bottom neighbor
41743                 u = a_map(x,y + 1,0);
41744                 v = a_map(x,y + 1,1);
41745                 if (u>=cx1 && u<patch_image.width() - cx2 &&
41746                     v>=cy1 + 1 && v<patch_image.height() + 1 - cy2) {
41747                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
41748                                   xp,yp,u - cx1,v - 1 - cy1,
41749                                   u,v,_patch_penalization,allow_identity,best_score);
41750                   if (s<best_score) { best_u = u; best_v = v - 1; best_score = s; }
41751                 }
41752               }
41753 
41754               float
41755                 dw = (float)patch_image.width(),
41756                 dh = (float)patch_image.height();
41757               for (unsigned int i = 0; i<nb_randoms; ++i) {
41758                 u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
41759                                                 std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
41760                 v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
41761                                                 std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
41762                 if (u!=best_u || v!=best_v) {
41763                   s = _matchpatch(in_this,in_patch,occ,patch_width,patch_height,_spectrum,
41764                                   xp,yp,u - cx1,v - cy1,
41765                                   u,v,_patch_penalization,allow_identity,best_score);
41766                   if (s<best_score) { best_u = u; best_v = v; best_score = s; }
41767                   dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f);
41768                 }
41769               }
41770 
41771               if (best_score<best_score0) {
41772                 if (_patch_penalization!=0) {
41773                   uintT &n_occ = occ(a_map(x,y,0),a_map(x,y,1));
41774                   if (n_occ) cimg_pragma_openmp(atomic) --n_occ;
41775                 }
41776                 a_map(x,y,0) = best_u;
41777                 a_map(x,y,1) = best_v;
41778                 score(x,y) = best_score;
41779                 is_updated(x,y) = 3;
41780               } else is_updated(x,y)&=~nmask;
41781               if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++occ(best_u,best_v);
41782             }
41783             cimg::srand(rng);
41784           }
41785         }
41786       }
41787 
41788       if (is_matching_score) score.move_to(matching_score);
41789       return a_map;
41790     }
41791 
41792     // Compute SSD between two patches in different images.
41793     static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<uintT>& occ,
41794                              const unsigned int psizew, const unsigned int psizeh,
41795                              const unsigned int psized, const unsigned int psizec,
41796                              const int x1, const int y1, const int z1,
41797                              const int x2, const int y2, const int z2,
41798                              const int xc, const int yc, const int zc,
41799                              const float patch_penalization,
41800                              const bool allow_identity,
41801                              const float max_score) { // 3D version
41802       if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2,(float)z1-z2)<patch_penalization)
41803         return cimg::type<float>::inf();
41804       const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2);
41805       const unsigned int psizewc = psizew*psizec;
41806       const ulongT
41807         offx1 = (ulongT)img1._width - psizewc,
41808         offx2 = (ulongT)img2._width - psizewc,
41809         offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width,
41810         offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width;
41811       float ssd = 0;
41812       for (unsigned int k = 0; k<psized; ++k) {
41813         for (unsigned int j = 0; j<psizeh; ++j) {
41814           for (unsigned int i = 0; i<psizewc; ++i)
41815             ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
41816           if (ssd>max_score) return max_score;
41817           p1+=offx1; p2+=offx2;
41818         }
41819         p1+=offy1; p2+=offy2;
41820       }
41821       return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
41822                                                patch_penalization*psizewc*psizeh*psized*occ(xc,yc,zc)/100);
41823     }
41824 
41825     static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<uintT>& occ,
41826                              const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec,
41827                              const int x1, const int y1,
41828                              const int x2, const int y2,
41829                              const int xc, const int yc,
41830                              const float patch_penalization,
41831                              const bool allow_identity,
41832                              const float max_score) { // 2D version
41833       if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2)<patch_penalization)
41834         return cimg::type<float>::inf();
41835       const T *p1 = img1.data(x1*psizec,y1), *p2 = img2.data(x2*psizec,y2);
41836       const unsigned int psizewc = psizew*psizec;
41837       const ulongT
41838         offx1 = (ulongT)img1._width - psizewc,
41839         offx2 = (ulongT)img2._width - psizewc;
41840       float ssd = 0;
41841       for (unsigned int j = 0; j<psizeh; ++j) {
41842         for (unsigned int i = 0; i<psizewc; ++i)
41843           ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
41844         if (ssd>max_score) return max_score;
41845         p1+=offx1; p2+=offx2;
41846       }
41847       return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
41848                                                patch_penalization*psizewc*psizeh*occ(xc,yc)/100);
41849     }
41850 
41851     //! Compute Euclidean distance function to a specified value.
41852     /**
41853         \param value Reference value.
41854         \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>.
41855         \note
41856         The distance transform implementation has been submitted by A. Meijster, and implements
41857         the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
41858                      "A general algorithm for computing distance transforms in linear time.",
41859                      In: Mathematical Morphology and its Applications to Image and Signal Processing,
41860                      J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
41861          The submitted code has then been modified to fit CImg coding style and constraints.
41862     **/
41863     CImg<T>& distance(const T& value, const unsigned int metric=2) {
41864       if (is_empty()) return *this;
41865       if (cimg::type<Tint>::string()!=pixel_type()) // For datatype < int
41866         return CImg<Tint>(*this,false).distance((Tint)value,metric).
41867           cut((Tint)cimg::type<T>::min(),(Tint)cimg::type<T>::max()).move_to(*this);
41868       bool is_value = false;
41869       cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning)
41870       if (!is_value) return fill(cimg::type<T>::max());
41871       switch (metric) {
41872       case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt);          // Chebyshev
41873       case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt);          // Manhattan
41874       case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt);          // Squared Euclidean
41875       default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt();  // Euclidean
41876       }
41877       return *this;
41878     }
41879 
41880     //! Compute distance to a specified value \newinstance.
41881     CImg<Tfloat> get_distance(const T& value, const unsigned int metric=2) const {
41882       return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
41883     }
41884 
41885     static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) {
41886       return (u*u - i*i + g[u] - g[i])/(2*(u - i));
41887     }
41888 
41889     static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) {
41890       return (x - i)*(x - i) + g[i];
41891     }
41892 
41893     static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) {
41894       return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2);
41895     }
41896 
41897     static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) {
41898       return (x<i?i - x:x - i) + g[i];
41899     }
41900 
41901     static longT _distance_sep_cdt(const longT i, const longT u, const longT *const g) {
41902       const longT h = (i + u)/2;
41903       if (g[i]<=g[u]) { return h<i + g[u]?i + g[u]:h; }
41904       return h<u - g[i]?h:u - g[i];
41905     }
41906 
41907     static longT _distance_dist_cdt(const longT x, const longT i, const longT *const g) {
41908       const longT d = x<i?i - x:x - i;
41909       return d<g[i]?g[i]:d;
41910     }
41911 
41912     static void _distance_scan(const unsigned int len,
41913                                const longT *const g,
41914                                longT (*const sep)(const longT, const longT, const longT *const),
41915                                longT (*const f)(const longT, const longT, const longT *const),
41916                                longT *const s,
41917                                longT *const t,
41918                                longT *const dt) {
41919       longT q = s[0] = t[0] = 0;
41920       for (int u = 1; u<(int)len; ++u) { // Forward scan
41921         while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
41922         if (q<0) { q = 0; s[0] = u; }
41923         else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }}
41924       }
41925       for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan
41926     }
41927 
41928     CImg<T>& _distance_core(longT (*const sep)(const longT, const longT, const longT *const),
41929                             longT (*const f)(const longT, const longT, const longT *const)) {
41930  // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why.
41931 #define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9)
41932 
41933       const ulongT wh = (ulongT)_width*_height;
41934 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
41935       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
41936 #endif
41937       cimg_forC(*this,c) {
41938         CImg<longT> g(_width), dt(_width), s(_width), t(_width);
41939         CImg<T> img = get_shared_channel(c);
41940 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
41941         cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
41942                                                                    _height*_depth>=16)
41943                            firstprivate(g,dt,s,t))
41944 #endif
41945         cimg_forYZ(*this,y,z) { // Over X-direction
41946           cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh);
41947           _distance_scan(_width,g,sep,f,s,t,dt);
41948           cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
41949         }
41950         if (_height>1) {
41951           g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
41952 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
41953           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41954                              cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && _width*_depth>=16)
41955                              firstprivate(g,dt,s,t))
41956 #endif
41957           cimg_forXZ(*this,x,z) { // Over Y-direction
41958             cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh);
41959             _distance_scan(_height,g,sep,f,s,t,dt);
41960             cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
41961           }
41962         }
41963         if (_depth>1) {
41964           g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
41965 #if cimg_use_openmp!=0 && !cimg_is_gcc49x
41966           cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
41967                              cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && _width*_height>=16)
41968                              firstprivate(g,dt,s,t))
41969 #endif
41970           cimg_forXY(*this,x,y) { // Over Z-direction
41971             cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh);
41972             _distance_scan(_depth,g,sep,f,s,t,dt);
41973             cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
41974           }
41975         }
41976       }
41977       return *this;
41978     }
41979 
41980     //! Compute chamfer distance to a specified value, with a custom metric.
41981     /**
41982        \param value Reference value.
41983        \param metric_mask Metric mask.
41984        \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
41985     **/
41986     template<typename t>
41987     CImg<T>& distance(const T& value, const CImg<t>& metric_mask) {
41988       if (is_empty()) return *this;
41989       bool is_value = false;
41990       cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
41991       if (!is_value) return fill(cimg::type<T>::max());
41992       const ulongT wh = (ulongT)_width*_height;
41993       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
41994       cimg_forC(*this,c) {
41995         CImg<T> img = get_shared_channel(c);
41996         cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
41997                            cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024))
41998         cimg_forXYZ(metric_mask,dx,dy,dz) {
41999           const t weight = metric_mask(dx,dy,dz);
42000           if (weight) {
42001             for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan
42002               for (int y = dy , ny = 0; y<height(); ++y,++ny) {
42003                 for (int x = dx, nx = 0; x<width(); ++x,++nx) {
42004                   const T dd = img(nx,ny,nz,0,wh) + weight;
42005                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
42006                 }
42007               }
42008             }
42009             for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan
42010               for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
42011                 for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
42012                   const T dd = img(nx,ny,nz,0,wh) + weight;
42013                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
42014                 }
42015               }
42016             }
42017           }
42018         }
42019       }
42020       return *this;
42021     }
42022 
42023     //! Compute chamfer distance to a specified value, with a custom metric \newinstance.
42024     template<typename t>
42025     CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const {
42026       return CImg<Tfloat>(*this,false).distance(value,metric_mask);
42027     }
42028 
42029     //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm).
42030     /**
42031        \param value Reference value.
42032        \param metric Field of distance potentials.
42033        \param is_high_connectivity Tells if the algorithm uses low or high connectivity.
42034        \param[out] return_path An image containing the nodes of the minimal path.
42035      **/
42036     template<typename t, typename to>
42037     CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
42038                                CImg<to>& return_path) {
42039       return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this);
42040     }
42041 
42042     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance.
42043     template<typename t, typename to>
42044     CImg<typename cimg::superset<t,long>::type>
42045     get_distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
42046                           CImg<to>& return_path) const {
42047       if (is_empty()) return return_path.assign();
42048       if (!is_sameXYZ(metric))
42049         throw CImgArgumentException(_cimg_instance
42050                                     "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) "
42051                                     "have incompatible dimensions.",
42052                                     cimg_instance,
42053                                     metric._width,metric._height,metric._depth,metric._spectrum);
42054       typedef typename cimg::superset<t,long>::type td;  // Type used for computing cumulative distances
42055       CImg<td> result(_width,_height,_depth,_spectrum), Q;
42056       CImg<boolT> is_queued(_width,_height,_depth,1);
42057       if (return_path) return_path.assign(_width,_height,_depth,_spectrum);
42058 
42059       cimg_forC(*this,c) {
42060         const CImg<T> img = get_shared_channel(c);
42061         const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
42062         CImg<td> res = result.get_shared_channel(c);
42063         CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>();
42064         unsigned int sizeQ = 0;
42065 
42066         // Detect initial seeds.
42067         is_queued.fill(0);
42068         cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) {
42069           Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z);
42070           res(x,y,z) = 0;
42071           if (path) path(x,y,z) = (to)0;
42072         }
42073 
42074         // Start distance propagation.
42075         while (sizeQ) {
42076 
42077           // Get and remove point with minimal potential from the queue.
42078           const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
42079           const td P = (td)-Q(0,0);
42080           Q._priority_queue_remove(sizeQ);
42081 
42082           // Update neighbors.
42083           td npot = 0;
42084           if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) {
42085             res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2;
42086           }
42087           if (x + 1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x + 1,y,z) + P),x + 1,y,z)) {
42088             res(x + 1,y,z) = npot; if (path) path(x + 1,y,z) = (to)1;
42089           }
42090           if (y - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) {
42091             res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8;
42092           }
42093           if (y + 1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y + 1,z) + P),x,y + 1,z)) {
42094             res(x,y + 1,z) = npot; if (path) path(x,y + 1,z) = (to)4;
42095           }
42096           if (z - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) {
42097             res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32;
42098           }
42099           if (z + 1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z + 1) + P),x,y,z + 1)) {
42100             res(x,y,z + 1) = npot; if (path) path(x,y,z + 1) = (to)16;
42101           }
42102 
42103           if (is_high_connectivity) {
42104             const float sqrt2 = std::sqrt(2.f), sqrt3 = std::sqrt(3.f);
42105 
42106             // Diagonal neighbors on slice z.
42107             if (x - 1>=0 && y - 1>=0 &&
42108                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) {
42109               res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10;
42110             }
42111             if (x + 1<width() && y - 1>=0 &&
42112                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) {
42113               res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9;
42114             }
42115             if (x - 1>=0 && y + 1<height() &&
42116                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y + 1,z) + P)),x - 1,y + 1,z)) {
42117               res(x - 1,y + 1,z) = npot; if (path) path(x - 1,y + 1,z) = (to)6;
42118             }
42119             if (x + 1<width() && y + 1<height() &&
42120                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y + 1,z) + P)),x + 1,y + 1,z)) {
42121               res(x + 1,y + 1,z) = npot; if (path) path(x + 1,y + 1,z) = (to)5;
42122             }
42123 
42124             if (z - 1>=0) { // Diagonal neighbors on slice z - 1
42125               if (x - 1>=0 &&
42126                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) {
42127                 res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34;
42128               }
42129               if (x + 1<width() &&
42130                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z - 1) + P)),x + 1,y,z - 1)) {
42131                 res(x + 1,y,z - 1) = npot; if (path) path(x + 1,y,z - 1) = (to)33;
42132               }
42133               if (y - 1>=0 &&
42134                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) {
42135                 res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40;
42136               }
42137               if (y + 1<height() &&
42138                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z - 1) + P)),x,y + 1,z - 1)) {
42139                 res(x,y + 1,z - 1) = npot; if (path) path(x,y + 1,z - 1) = (to)36;
42140               }
42141               if (x - 1>=0 && y - 1>=0 &&
42142                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)),
42143                                            x - 1,y - 1,z - 1)) {
42144                 res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42;
42145               }
42146               if (x + 1<width() && y - 1>=0 &&
42147                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)),
42148                                            x + 1,y - 1,z - 1)) {
42149                 res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41;
42150               }
42151               if (x - 1>=0 && y + 1<height() &&
42152                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z - 1) + P)),
42153                                            x - 1,y + 1,z - 1)) {
42154                 res(x - 1,y + 1,z - 1) = npot; if (path) path(x - 1,y + 1,z - 1) = (to)38;
42155               }
42156               if (x + 1<width() && y + 1<height() &&
42157                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z - 1) + P)),
42158                                            x + 1,y + 1,z - 1)) {
42159                 res(x + 1,y + 1,z - 1) = npot; if (path) path(x + 1,y + 1,z - 1) = (to)37;
42160               }
42161             }
42162 
42163             if (z + 1<depth()) { // Diagonal neighbors on slice z + 1
42164               if (x - 1>=0 &&
42165                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) {
42166                 res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18;
42167               }
42168               if (x + 1<width() &&
42169                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z + 1) + P)),x + 1,y,z + 1)) {
42170                 res(x + 1,y,z + 1) = npot; if (path) path(x + 1,y,z + 1) = (to)17;
42171               }
42172               if (y - 1>=0 &&
42173                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) {
42174                 res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24;
42175               }
42176               if (y + 1<height() &&
42177                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z + 1) + P)),x,y + 1,z + 1)) {
42178                 res(x,y + 1,z + 1) = npot; if (path) path(x,y + 1,z + 1) = (to)20;
42179               }
42180               if (x - 1>=0 && y - 1>=0 &&
42181                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)),
42182                                            x - 1,y - 1,z + 1)) {
42183                 res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26;
42184               }
42185               if (x + 1<width() && y - 1>=0 &&
42186                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)),
42187                                            x + 1,y - 1,z + 1)) {
42188                 res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25;
42189               }
42190               if (x - 1>=0 && y + 1<height() &&
42191                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z + 1) + P)),
42192                                            x - 1,y + 1,z + 1)) {
42193                 res(x - 1,y + 1,z + 1) = npot; if (path) path(x - 1,y + 1,z + 1) = (to)22;
42194               }
42195               if (x + 1<width() && y + 1<height() &&
42196                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z + 1) + P)),
42197                                            x + 1,y + 1,z + 1)) {
42198                 res(x + 1,y + 1,z + 1) = npot; if (path) path(x + 1,y + 1,z + 1) = (to)21;
42199               }
42200             }
42201           }
42202         }
42203       }
42204       return result;
42205     }
42206 
42207     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \overloading.
42208     template<typename t>
42209     CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric,
42210                                const bool is_high_connectivity=false) {
42211       return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this);
42212     }
42213 
42214     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance.
42215     template<typename t>
42216     CImg<Tfloat> get_distance_dijkstra(const T& value, const CImg<t>& metric,
42217                                        const bool is_high_connectivity=false) const {
42218       CImg<T> return_path;
42219       return get_distance_dijkstra(value,metric,is_high_connectivity,return_path);
42220     }
42221 
42222     //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
42223     /**
42224        \param value Reference value.
42225        \param metric Field of distance potentials.
42226      **/
42227     template<typename t>
42228     CImg<T>& distance_eikonal(const T& value, const CImg<t>& metric) {
42229       return get_distance_eikonal(value,metric).move_to(*this);
42230     }
42231 
42232     //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
42233     template<typename t>
42234     CImg<Tfloat> get_distance_eikonal(const T& value, const CImg<t>& metric) const {
42235       if (is_empty()) return *this;
42236       if (!is_sameXYZ(metric))
42237         throw CImgArgumentException(_cimg_instance
42238                                     "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have "
42239                                     "incompatible dimensions.",
42240                                     cimg_instance,
42241                                     metric._width,metric._height,metric._depth,metric._spectrum);
42242       CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q;
42243       CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen
42244 
42245       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state))
42246       cimg_forC(*this,c) {
42247         const CImg<T> img = get_shared_channel(c);
42248         const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
42249         CImg<Tfloat> res = result.get_shared_channel(c);
42250         unsigned int sizeQ = 0;
42251         state.fill(-1);
42252 
42253         // Detect initial seeds.
42254         Tfloat *ptr1 = res._data; char *ptr2 = state._data;
42255         cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; }
42256 
42257         // Initialize seeds neighbors.
42258         ptr2 = state._data;
42259         cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) {
42260           if (x - 1>=0 && state(x - 1,y,z)==-1) {
42261             const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
42262             Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
42263           }
42264           if (x + 1<width() && state(x + 1,y,z)==-1) {
42265             const Tfloat dist = res(x + 1,y,z) = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
42266             Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
42267           }
42268           if (y - 1>=0 && state(x,y - 1,z)==-1) {
42269             const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
42270             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
42271           }
42272           if (y + 1<height() && state(x,y + 1,z)==-1) {
42273             const Tfloat dist = res(x,y + 1,z) = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
42274             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
42275           }
42276           if (z - 1>=0 && state(x,y,z - 1)==-1) {
42277             const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
42278             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
42279           }
42280           if (z + 1<depth() && state(x,y,z + 1)==-1) {
42281             const Tfloat dist = res(x,y,z + 1) = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
42282             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
42283           }
42284         }
42285 
42286         // Propagate front.
42287         while (sizeQ) {
42288           int x = -1, y = -1, z = -1;
42289           while (sizeQ && x<0) {
42290             x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3);
42291             Q._priority_queue_remove(sizeQ);
42292             if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1;
42293           }
42294           if (x>=0) {
42295             if (x - 1>=0 && state(x - 1,y,z)!=1) {
42296               const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
42297               if (dist<res(x - 1,y,z)) {
42298                 res(x - 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
42299               }
42300             }
42301             if (x + 1<width() && state(x + 1,y,z)!=1) {
42302               const Tfloat dist = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
42303               if (dist<res(x + 1,y,z)) {
42304                 res(x + 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
42305               }
42306             }
42307             if (y - 1>=0 && state(x,y - 1,z)!=1) {
42308               const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
42309               if (dist<res(x,y - 1,z)) {
42310                 res(x,y - 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
42311               }
42312             }
42313             if (y + 1<height() && state(x,y + 1,z)!=1) {
42314               const Tfloat dist = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
42315               if (dist<res(x,y + 1,z)) {
42316                 res(x,y + 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
42317               }
42318             }
42319             if (z - 1>=0 && state(x,y,z - 1)!=1) {
42320               const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
42321               if (dist<res(x,y,z - 1)) {
42322                 res(x,y,z - 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
42323               }
42324             }
42325             if (z + 1<depth() && state(x,y,z + 1)!=1) {
42326               const Tfloat dist = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
42327               if (dist<res(x,y,z + 1)) {
42328                 res(x,y,z + 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
42329               }
42330             }
42331           }
42332         }
42333       }
42334       return result;
42335     }
42336 
42337     // Locally solve eikonal equation.
42338     Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
42339                               const int x=0, const int y=0, const int z=0) const {
42340       const Tfloat M = (Tfloat)cimg::type<T>::max();
42341       T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M);
42342       Tfloat root = 0;
42343       if (_depth>1) { // 3D
42344         T
42345           T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M),
42346           T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M);
42347         if (T1>T2) cimg::swap(T1,T2);
42348         if (T2>T3) cimg::swap(T2,T3);
42349         if (T1>T2) cimg::swap(T1,T2);
42350         if (P<=0) return (Tfloat)T1;
42351         if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root))
42352           return std::max((Tfloat)T3,root);
42353         if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
42354           return std::max((Tfloat)T2,root);
42355         return P + T1;
42356       } else if (_height>1) { // 2D
42357         T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M);
42358         if (T1>T2) cimg::swap(T1,T2);
42359         if (P<=0) return (Tfloat)T1;
42360         if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
42361           return std::max((Tfloat)T2,root);
42362         return P + T1;
42363       } else { // 1D
42364         if (P<=0) return (Tfloat)T1;
42365         return P + T1;
42366       }
42367       return 0;
42368     }
42369 
42370     // Find max root of a 2nd-order polynomial.
42371     static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) {
42372       const Tfloat delta = b*b - 4*a*c;
42373       if (delta<0) return false;
42374       root = 0.5f*(-b + std::sqrt(delta))/a;
42375       return true;
42376     }
42377 
42378     // Insert new point in heap.
42379     template<typename t>
42380     void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value,
42381                                     const unsigned int x, const unsigned int y, const unsigned int z) {
42382       if (state(x,y,z)>0) return;
42383       state(x,y,z) = 0;
42384       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
42385       (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z;
42386       for (unsigned int pos = siz - 1, par = 0; pos && value>(t)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
42387         cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
42388         cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
42389       }
42390     }
42391 
42392     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE.
42393     /**
42394        \param nb_iterations Number of PDE iterations.
42395        \param band_size Size of the narrow band.
42396        \param time_step Time step of the PDE iterations.
42397     **/
42398     CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
42399       if (is_empty()) return *this;
42400       CImg<Tfloat> velocity(*this,false);
42401       for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
42402         Tfloat *ptrd = velocity._data, veloc_max = 0;
42403         if (_depth>1) { // 3D
42404           CImg_3x3x3(I,Tfloat);
42405           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
42406             const Tfloat
42407               gx = (Incc - Ipcc)/2,
42408               gy = (Icnc - Icpc)/2,
42409               gz = (Iccn - Iccp)/2,
42410               sgn = -cimg::sign(Iccc),
42411               ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
42412               iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
42413               iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
42414               ng = 1e-5f + cimg::hypot(gx,gy,gz),
42415               ngx = gx/ng,
42416               ngy = gy/ng,
42417               ngz = gz/ng,
42418               veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
42419             *(ptrd++) = veloc;
42420             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
42421           } else *(ptrd++) = 0;
42422         } else { // 2D version
42423           CImg_3x3(I,Tfloat);
42424           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
42425             const Tfloat
42426               gx = (Inc - Ipc)/2,
42427               gy = (Icn - Icp)/2,
42428               sgn = -cimg::sign(Icc),
42429               ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
42430               iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
42431               ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)),
42432               ngx = gx/ng,
42433               ngy = gy/ng,
42434               veloc = sgn*(ngx*ix + ngy*iy - 1);
42435             *(ptrd++) = veloc;
42436             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
42437           } else *(ptrd++) = 0;
42438         }
42439         if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
42440       }
42441       return *this;
42442     }
42443 
42444     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance.
42445     CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
42446                                       const float time_step=0.5f) const {
42447       return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
42448     }
42449 
42450     //! Compute Haar multiscale wavelet transform.
42451     /**
42452        \param axis Axis considered for the transform.
42453        \param invert Set inverse of direct transform.
42454        \param nb_scales Number of scales used for the transform.
42455     **/
42456     CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
42457       return get_haar(axis,invert,nb_scales).move_to(*this);
42458     }
42459 
42460     //! Compute Haar multiscale wavelet transform \newinstance.
42461     CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
42462       if (is_empty() || !nb_scales) return +*this;
42463       CImg<Tfloat> res;
42464       const Tfloat sqrt2 = std::sqrt(2.f);
42465       if (nb_scales==1) {
42466         switch (cimg::lowercase(axis)) { // Single scale transform
42467         case 'x' : {
42468           const unsigned int w = _width/2;
42469           if (w) {
42470             if ((w%2) && w!=1)
42471               throw CImgInstanceException(_cimg_instance
42472                                           "haar(): Sub-image width %u is not even.",
42473                                           cimg_instance,
42474                                           w);
42475 
42476             res.assign(_width,_height,_depth,_spectrum);
42477             if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
42478               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
42479                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
42480                 res(x2++,y,z,c) = (val0 - val1)/sqrt2;
42481                 res(x2++,y,z,c) = (val0 + val1)/sqrt2;
42482               }
42483             } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
42484               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
42485                 const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
42486                 res(x,y,z,c) = (val0 + val1)/sqrt2;
42487                 res(xw,y,z,c) = (val1 - val0)/sqrt2;
42488               }
42489             }
42490           } else return *this;
42491         } break;
42492         case 'y' : {
42493           const unsigned int h = _height/2;
42494           if (h) {
42495             if ((h%2) && h!=1)
42496               throw CImgInstanceException(_cimg_instance
42497                                           "haar(): Sub-image height %u is not even.",
42498                                           cimg_instance,
42499                                           h);
42500 
42501             res.assign(_width,_height,_depth,_spectrum);
42502             if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
42503               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
42504                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
42505                 res(x,y2++,z,c) = (val0 - val1)/sqrt2;
42506                 res(x,y2++,z,c) = (val0 + val1)/sqrt2;
42507               }
42508             } else cimg_forXZC(*this,x,z,c) {
42509               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
42510                 const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
42511                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
42512                 res(x,yh,z,c) = (val1 - val0)/sqrt2;
42513               }
42514             }
42515           } else return *this;
42516         } break;
42517         case 'z' : {
42518           const unsigned int d = _depth/2;
42519           if (d) {
42520             if ((d%2) && d!=1)
42521               throw CImgInstanceException(_cimg_instance
42522                                           "haar(): Sub-image depth %u is not even.",
42523                                           cimg_instance,
42524                                           d);
42525 
42526             res.assign(_width,_height,_depth,_spectrum);
42527             if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
42528               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
42529                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
42530                 res(x,y,z2++,c) = (val0 - val1)/sqrt2;
42531                 res(x,y,z2++,c) = (val0 + val1)/sqrt2;
42532               }
42533             } else cimg_forXYC(*this,x,y,c) {
42534               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
42535                 const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
42536                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
42537                 res(x,y,zd,c) = (val1 - val0)/sqrt2;
42538               }
42539             }
42540           } else return *this;
42541         } break;
42542         default :
42543           throw CImgArgumentException(_cimg_instance
42544                                       "haar(): Invalid specified axis '%c' "
42545                                       "(should be { x | y | z }).",
42546                                       cimg_instance,
42547                                       axis);
42548         }
42549       } else { // Multi-scale version
42550         if (invert) {
42551           res.assign(*this,false);
42552           switch (cimg::lowercase(axis)) {
42553           case 'x' : {
42554             unsigned int w = _width;
42555             for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
42556             for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w - 1).get_haar('x',true,1));
42557           } break;
42558           case 'y' : {
42559             unsigned int h = _width;
42560             for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
42561             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));
42562           } break;
42563           case 'z' : {
42564             unsigned int d = _depth;
42565             for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
42566             for (d = d?d:1; d<=_depth; d*=2)
42567               res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',true,1));
42568           } break;
42569           default :
42570             throw CImgArgumentException(_cimg_instance
42571                                         "haar(): Invalid specified axis '%c' "
42572                                         "(should be { x | y | z }).",
42573                                         cimg_instance,
42574                                         axis);
42575           }
42576         } else { // Direct transform
42577           res = get_haar(axis,false,1);
42578           switch (cimg::lowercase(axis)) {
42579           case 'x' : {
42580             for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
42581               res.draw_image(res.get_crop(0,w - 1).get_haar('x',false,1));
42582           } break;
42583           case 'y' : {
42584             for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
42585               res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',false,1));
42586           } break;
42587           case 'z' : {
42588             for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
42589               res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',false,1));
42590           } break;
42591           default :
42592             throw CImgArgumentException(_cimg_instance
42593                                         "haar(): Invalid specified axis '%c' "
42594                                         "(should be { x | y | z }).",
42595                                         cimg_instance,
42596                                         axis);
42597           }
42598         }
42599       }
42600       return res;
42601     }
42602 
42603     //! Compute Haar multiscale wavelet transform \overloading.
42604     /**
42605        \param invert Set inverse of direct transform.
42606        \param nb_scales Number of scales used for the transform.
42607     **/
42608     CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
42609       return get_haar(invert,nb_scales).move_to(*this);
42610     }
42611 
42612     //! Compute Haar multiscale wavelet transform \newinstance.
42613     CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
42614       CImg<Tfloat> res;
42615       if (nb_scales==1) { // Single scale transform
42616         if (_width>1) get_haar('x',invert,1).move_to(res);
42617         if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
42618         if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
42619         if (res) return res;
42620       } else { // Multi-scale transform
42621         if (invert) { // Inverse transform
42622           res.assign(*this,false);
42623           if (_width>1) {
42624             if (_height>1) {
42625               if (_depth>1) {
42626                 unsigned int w = _width, h = _height, d = _depth;
42627                 for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
42628                 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)
42629                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).get_haar(true,1));
42630               } else {
42631                 unsigned int w = _width, h = _height;
42632                 for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
42633                 for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
42634                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).get_haar(true,1));
42635               }
42636             } else {
42637               if (_depth>1) {
42638                 unsigned int w = _width, d = _depth;
42639                 for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
42640                 for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
42641                   res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).get_haar(true,1));
42642               } else {
42643                 unsigned int w = _width;
42644                 for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
42645                 for (w = w?w:1; w<=_width; w*=2)
42646                   res.draw_image(res.get_crop(0,0,0,w - 1,0,0).get_haar(true,1));
42647               }
42648             }
42649           } else {
42650             if (_height>1) {
42651               if (_depth>1) {
42652                 unsigned int h = _height, d = _depth;
42653                 for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
42654                 for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
42655                   res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).get_haar(true,1));
42656               } else {
42657                 unsigned int h = _height;
42658                 for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
42659                 for (h = h?h:1; h<=_height; h*=2)
42660                   res.draw_image(res.get_crop(0,0,0,0,h - 1,0).get_haar(true,1));
42661               }
42662             } else {
42663               if (_depth>1) {
42664                 unsigned int d = _depth;
42665                 for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
42666                 for (d = d?d:1; d<=_depth; d*=2)
42667                   res.draw_image(res.get_crop(0,0,0,0,0,d - 1).get_haar(true,1));
42668               } else return *this;
42669             }
42670           }
42671         } else { // Direct transform
42672           res = get_haar(false,1);
42673           if (_width>1) {
42674             if (_height>1) {
42675               if (_depth>1)
42676                 for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales;
42677                      ++s, w/=2, h/=2, d/=2)
42678                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).haar(false,1));
42679               else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
42680                      res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).haar(false,1));
42681             } else {
42682               if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
42683                               res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).haar(false,1));
42684               else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
42685                      res.draw_image(res.get_crop(0,0,0,w - 1,0,0).haar(false,1));
42686             }
42687           } else {
42688             if (_height>1) {
42689               if (_depth>1)
42690                 for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
42691                   res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).haar(false,1));
42692               else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
42693                      res.draw_image(res.get_crop(0,0,0,0,h - 1,0).haar(false,1));
42694             } else {
42695               if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
42696                               res.draw_image(res.get_crop(0,0,0,0,0,d - 1).haar(false,1));
42697               else return *this;
42698             }
42699           }
42700         }
42701         return res;
42702       }
42703       return *this;
42704     }
42705 
42706     //! Compute 1D Fast Fourier Transform, along a specified axis.
42707     /**
42708        \param axis Axis along which the FFT is computed.
42709        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
42710     **/
42711     CImgList<Tfloat> get_FFT(const char axis, const bool is_inverse=false) const {
42712       CImgList<Tfloat> res(*this,CImg<Tfloat>());
42713       CImg<Tfloat>::FFT(res[0],res[1],axis,is_inverse);
42714       return res;
42715     }
42716 
42717     //! Compute n-D Fast Fourier Transform.
42718     /*
42719       \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
42720     **/
42721     CImgList<Tfloat> get_FFT(const bool is_inverse=false) const {
42722       CImgList<Tfloat> res(*this,CImg<Tfloat>());
42723       CImg<Tfloat>::FFT(res[0],res[1],is_inverse);
42724       return res;
42725     }
42726 
42727     //! Compute 1D Fast Fourier Transform, along a specified axis.
42728     /**
42729        \param[in,out] real Real part of the pixel values.
42730        \param[in,out] imag Imaginary part of the pixel values.
42731        \param axis Axis along which the FFT is computed.
42732        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
42733     **/
42734     static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_inverse=false,
42735                     const unsigned int nb_threads=0) {
42736       if (!real)
42737         throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
42738                                     pixel_type());
42739       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
42740       if (!real.is_sameXYZC(imag))
42741         throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
42742                                     "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
42743                                     pixel_type(),
42744                                     real._width,real._height,real._depth,real._spectrum,real._data,
42745                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
42746       const char _axis = cimg::lowercase(axis);
42747       if (_axis!='x' && _axis!='y' && _axis!='z')
42748         throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
42749                                     "(%u,%u,%u,%u) "
42750                                     "(should be { x | y | z }).",
42751                                     pixel_type(),axis,
42752                                     real._width,real._height,real._depth,real._spectrum);
42753       cimg::unused(nb_threads);
42754 #ifdef cimg_use_fftw3
42755       cimg::mutex(12);
42756 #ifndef cimg_use_fftw3_singlethread
42757       fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
42758 #endif
42759       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
42760       if (!data_in)
42761         throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
42762                                     "for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
42763                                     pixel_type(),
42764                                     cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth),
42765                                     real._width,real._height,real._depth,real._spectrum);
42766       double *const ptrf = (double*)data_in;
42767       fftw_plan data_plan =
42768         _axis=='x'?fftw_plan_many_dft(1,(int*)&real._width,real.height()*real.depth(),
42769                                       data_in,0,1,real.width(),
42770                                       data_in,0,1,real.width(),
42771                                       is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
42772         _axis=='y'?fftw_plan_many_dft(1,(int*)&real._height,real.width()*real.depth(),
42773                                       data_in,0,1,real.height(),
42774                                       data_in,0,1,real.height(),
42775                                       is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
42776         fftw_plan_many_dft(1,(int*)&real._depth,real.width()*real.height(),
42777                            data_in,0,1,real.depth(),
42778                            data_in,0,1,real.depth(),
42779                            is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
42780       cimg_forC(real,c) {
42781         CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
42782         switch (_axis) {
42783         case 'x' :
42784           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
42785             cimg_forXYZ(realc,x,y,z) {
42786             const ulongT
42787               i = realc.offset(x,y,z),
42788               j = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height);
42789             ptrf[j] = (double)realc[i];
42790             ptrf[j + 1] = (double)imagc[i];
42791           }
42792           break;
42793         case 'y' :
42794           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
42795             cimg_forXYZ(realc,x,y,z) {
42796             const ulongT
42797               i = realc.offset(x,y,z),
42798               j = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height);
42799             ptrf[j] = (double)realc[i];
42800             ptrf[j + 1] = (double)imagc[i];
42801           }
42802           break;
42803         default :
42804           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
42805             cimg_forXYZ(realc,x,y,z) {
42806             const ulongT
42807               i = realc.offset(x,y,z),
42808               j = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth);
42809             ptrf[j] = (double)realc[i];
42810             ptrf[j + 1] = (double)imagc[i];
42811           }
42812         }
42813 
42814         fftw_execute(data_plan);
42815 
42816         const double a = is_inverse?1.0/(_axis=='x'?real.width():_axis=='y'?real.height():real.depth()):1.0;
42817         switch (_axis) {
42818         case 'x' :
42819           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
42820             cimg_forXYZ(realc,x,y,z) {
42821             const ulongT
42822               i = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height),
42823               j = realc.offset(x,y,z);
42824             realc[j] = (T)(a*ptrf[i]);
42825             imagc[j] = (T)(a*ptrf[i + 1]);
42826           }
42827           break;
42828         case 'y' :
42829           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
42830             cimg_forXYZ(realc,x,y,z) {
42831             const ulongT
42832               i = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height),
42833               j = realc.offset(x,y,z);
42834             realc[j] = (T)(a*ptrf[i]);
42835             imagc[j] = (T)(a*ptrf[i + 1]);
42836           }
42837           break;
42838         default :
42839           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
42840             cimg_forXYZ(realc,x,y,z) {
42841             const ulongT
42842               i = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth),
42843               j = realc.offset(x,y,z);
42844             realc[j] = (T)(a*ptrf[i]);
42845             imagc[j] = (T)(a*ptrf[i + 1]);
42846           }
42847         }
42848       }
42849 
42850       fftw_destroy_plan(data_plan);
42851       fftw_free(data_in);
42852 #ifndef cimg_use_fftw3_singlethread
42853       fftw_cleanup_threads();
42854 #endif
42855       cimg::mutex(12,0);
42856 #else
42857       switch (_axis) {
42858       case 'x' : { // Fourier along X, using built-in functions
42859         const unsigned int N = real._width, N2 = N>>1;
42860         if (((N - 1)&N) && N!=1)
42861           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
42862                                       "have non 2^N dimension along the X-axis.",
42863                                       pixel_type(),
42864                                       real._width,real._height,real._depth,real._spectrum);
42865 
42866         for (unsigned int i = 0, j = 0; i<N2; ++i) {
42867           if (j>i) cimg_forYZC(real,y,z,c) {
42868               cimg::swap(real(i,y,z,c),real(j,y,z,c));
42869               cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
42870               if (j<N2) {
42871                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
42872                 cimg::swap(real(ri,y,z,c),real(rj,y,z,c));
42873                 cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
42874               }
42875             }
42876           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
42877         }
42878         for (unsigned int delta = 2; delta<=N; delta<<=1) {
42879           const unsigned int delta2 = delta>>1;
42880           for (unsigned int i = 0; i<N; i+=delta) {
42881             float wr = 1, wi = 0;
42882             const float
42883               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
42884               ca = (float)std::cos(angle),
42885               sa = (float)std::sin(angle);
42886             for (unsigned int k = 0; k<delta2; ++k) {
42887               const unsigned int j = i + k, nj = j + delta2;
42888               cimg_forYZC(real,y,z,c) {
42889                 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);
42890                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
42891                 nir = (T)(ir - tmpr);
42892                 nii = (T)(ii - tmpi);
42893                 ir+=(T)tmpr;
42894                 ii+=(T)tmpi;
42895               }
42896               const float nwr = wr*ca-wi*sa;
42897               wi = wi*ca + wr*sa;
42898               wr = nwr;
42899             }
42900           }
42901         }
42902         if (is_inverse) { real/=N; imag/=N; }
42903       } break;
42904       case 'y' : { // Fourier along Y, using built-in functions
42905         const unsigned int N = real._height, N2 = N>>1;
42906         if (((N - 1)&N) && N!=1)
42907           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
42908                                       "have non 2^N dimension along the Y-axis.",
42909                                       pixel_type(),
42910                                       real._width,real._height,real._depth,real._spectrum);
42911 
42912         for (unsigned int i = 0, j = 0; i<N2; ++i) {
42913           if (j>i) cimg_forXZC(real,x,z,c) {
42914               cimg::swap(real(x,i,z,c),real(x,j,z,c));
42915               cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
42916               if (j<N2) {
42917                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
42918                 cimg::swap(real(x,ri,z,c),real(x,rj,z,c));
42919                 cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
42920               }
42921             }
42922           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
42923         }
42924         for (unsigned int delta = 2; delta<=N; delta<<=1) {
42925           const unsigned int delta2 = (delta>>1);
42926           for (unsigned int i = 0; i<N; i+=delta) {
42927             float wr = 1, wi = 0;
42928             const float
42929               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
42930               ca = (float)std::cos(angle),
42931               sa = (float)std::sin(angle);
42932             for (unsigned int k = 0; k<delta2; ++k) {
42933               const unsigned int j = i + k, nj = j + delta2;
42934               cimg_forXZC(real,x,z,c) {
42935                 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);
42936                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
42937                 nir = (T)(ir - tmpr);
42938                 nii = (T)(ii - tmpi);
42939                 ir+=(T)tmpr;
42940                 ii+=(T)tmpi;
42941               }
42942               const float nwr = wr*ca-wi*sa;
42943               wi = wi*ca + wr*sa;
42944               wr = nwr;
42945             }
42946           }
42947         }
42948         if (is_inverse) { real/=N; imag/=N; }
42949       } break;
42950       default : { // Fourier along Z, using built-in functions
42951         const unsigned int N = real._depth, N2 = N>>1;
42952         if (((N - 1)&N) && N!=1)
42953           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
42954                                       "have non 2^N dimension along the Z-axis.",
42955                                       pixel_type(),
42956                                       real._width,real._height,real._depth,real._spectrum);
42957 
42958         for (unsigned int i = 0, j = 0; i<N2; ++i) {
42959           if (j>i) cimg_forXYC(real,x,y,c) {
42960               cimg::swap(real(x,y,i,c),real(x,y,j,c));
42961               cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
42962               if (j<N2) {
42963                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
42964                 cimg::swap(real(x,y,ri,c),real(x,y,rj,c));
42965                 cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
42966               }
42967             }
42968           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
42969         }
42970         for (unsigned int delta = 2; delta<=N; delta<<=1) {
42971           const unsigned int delta2 = (delta>>1);
42972           for (unsigned int i = 0; i<N; i+=delta) {
42973             float wr = 1, wi = 0;
42974             const float
42975               angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
42976               ca = (float)std::cos(angle),
42977               sa = (float)std::sin(angle);
42978             for (unsigned int k = 0; k<delta2; ++k) {
42979               const unsigned int j = i + k, nj = j + delta2;
42980               cimg_forXYC(real,x,y,c) {
42981                 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);
42982                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
42983                 nir = (T)(ir - tmpr);
42984                 nii = (T)(ii - tmpi);
42985                 ir+=(T)tmpr;
42986                 ii+=(T)tmpi;
42987               }
42988               const float nwr = wr*ca-wi*sa;
42989               wi = wi*ca + wr*sa;
42990               wr = nwr;
42991             }
42992           }
42993         }
42994         if (is_inverse) { real/=N; imag/=N; }
42995       } break;
42996       }
42997 #endif
42998     }
42999 
43000     //! Compute n-D Fast Fourier Transform.
43001     /**
43002        \param[in,out] real Real part of the pixel values.
43003        \param[in,out] imag Imaginary part of the pixel values.
43004        \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
43005        \param nb_threads Number of parallel threads used for the computation.
43006          Use \c 0 to set this to the number of available cpus.
43007     **/
43008     static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_inverse=false,
43009                     const unsigned int nb_threads=0) {
43010       if (!real)
43011         throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
43012                                     pixel_type());
43013       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
43014       if (!real.is_sameXYZC(imag))
43015         throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
43016                                     "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
43017                                     pixel_type(),
43018                                     real._width,real._height,real._depth,real._spectrum,real._data,
43019                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
43020       cimg::unused(nb_threads);
43021 #ifdef cimg_use_fftw3
43022       cimg::mutex(12);
43023 #ifndef cimg_use_fftw3_singlethread
43024       fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
43025 #endif
43026       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
43027       if (!data_in)
43028         throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
43029                                     "for computing FFT of image (%u,%u,%u,%u).",
43030                                     pixel_type(),
43031                                     cimg::strbuffersize(sizeof(fftw_complex)*real._width*
43032                                                         real._height*real._depth*real._spectrum),
43033                                     real._width,real._height,real._depth,real._spectrum);
43034       double *const ptrf = (double*)data_in;
43035       fftw_plan data_plan =
43036         real._depth>1?fftw_plan_dft_3d(real._depth,real._height,real._width,data_in,data_in,
43037                                        is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
43038         real._height>1?fftw_plan_dft_2d(real._height,real._width,data_in,data_in,
43039                                         is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
43040         fftw_plan_dft_1d(real._width,data_in,data_in,
43041                          is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
43042       cimg_forC(real,c) {
43043         CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
43044         cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43045           cimg_rofoff(realc,i) { const ulongT i2 = 2*i; ptrf[i2] = (double)realc[i]; ptrf[i2 + 1] = (double)imagc[i]; }
43046         fftw_execute(data_plan);
43047         if (is_inverse) {
43048           const double a = 1.0/(real.width()*real.height()*real.depth());
43049           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43050             cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)(a*ptrf[i2]); imagc[i] = (T)(a*ptrf[i2 + 1]); }
43051         } else
43052           cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
43053             cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)ptrf[i2]; imagc[i] = (T)ptrf[i2 + 1]; }
43054       }
43055       fftw_destroy_plan(data_plan);
43056       fftw_free(data_in);
43057 #ifndef cimg_use_fftw3_singlethread
43058       fftw_cleanup_threads();
43059 #endif
43060       cimg::mutex(12,0);
43061 #else
43062       if (real._depth>1) FFT(real,imag,'z',is_inverse);
43063       if (real._height>1) FFT(real,imag,'y',is_inverse);
43064       if (real._width>1) FFT(real,imag,'x',is_inverse);
43065 #endif
43066     }
43067 
43068     //@}
43069     //-------------------------------------
43070     //
43071     //! \name 3D Objects Management
43072     //@{
43073     //-------------------------------------
43074 
43075     //! Rotate 3D object's vertices.
43076     /**
43077        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
43078        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
43079        \param z Z-coordinate of the rotation axis, or second quaternion coordinate.
43080        \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
43081        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
43082     **/
43083     CImg<T>& rotate_object3d(const float x, const float y, const float z, const float w,
43084                              const bool is_quaternion=false) {
43085       return get_rotate_object3d(x,y,z,w,is_quaternion).move_to(*this);
43086     }
43087 
43088     CImg<Tfloat> get_rotate_object3d(const float x, const float y, const float z, const float w,
43089                                      const bool is_quaternion=false) const {
43090       if (_height!=3 || _depth>1 || _spectrum>1)
43091         throw CImgInstanceException(_cimg_instance
43092                                     "rotate_object3d(): Instance is not a set of 3D vertices.",
43093                                     cimg_instance);
43094       return CImg<Tfloat>::rotation_matrix(x,y,z,w,is_quaternion)**this;
43095     }
43096 
43097     //! Shift 3D object's vertices.
43098     /**
43099        \param tx X-coordinate of the 3D displacement vector.
43100        \param ty Y-coordinate of the 3D displacement vector.
43101        \param tz Z-coordinate of the 3D displacement vector.
43102     **/
43103     CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
43104       if (_height!=3 || _depth>1 || _spectrum>1)
43105         throw CImgInstanceException(_cimg_instance
43106                                     "shift_object3d(): Instance is not a set of 3D vertices.",
43107                                     cimg_instance);
43108 
43109       get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
43110       return *this;
43111     }
43112 
43113     //! Shift 3D object's vertices \newinstance.
43114     CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
43115       return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
43116     }
43117 
43118     //! Shift 3D object's vertices, so that it becomes centered.
43119     /**
43120        \note The object center is computed as its barycenter.
43121     **/
43122     CImg<T>& shift_object3d() {
43123       if (_height!=3 || _depth>1 || _spectrum>1)
43124         throw CImgInstanceException(_cimg_instance
43125                                     "shift_object3d(): Instance is not a set of 3D vertices.",
43126                                     cimg_instance);
43127 
43128       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
43129       float
43130         xm, xM = (float)xcoords.max_min(xm),
43131         ym, yM = (float)ycoords.max_min(ym),
43132         zm, zM = (float)zcoords.max_min(zm);
43133       xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
43134       return *this;
43135     }
43136 
43137     //! Shift 3D object's vertices, so that it becomes centered \newinstance.
43138     CImg<Tfloat> get_shift_object3d() const {
43139       return CImg<Tfloat>(*this,false).shift_object3d();
43140     }
43141 
43142     //! Resize 3D object.
43143     /**
43144        \param sx Width of the 3D object's bounding box.
43145        \param sy Height of the 3D object's bounding box.
43146        \param sz Depth of the 3D object's bounding box.
43147     **/
43148     CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
43149       if (_height!=3 || _depth>1 || _spectrum>1)
43150         throw CImgInstanceException(_cimg_instance
43151                                     "resize_object3d(): Instance is not a set of 3D vertices.",
43152                                     cimg_instance);
43153 
43154       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
43155       float
43156         xm, xM = (float)xcoords.max_min(xm),
43157         ym, yM = (float)ycoords.max_min(ym),
43158         zm, zM = (float)zcoords.max_min(zm);
43159       if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
43160       if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
43161       if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
43162       return *this;
43163     }
43164 
43165     //! Resize 3D object \newinstance.
43166     CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
43167       return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
43168     }
43169 
43170     //! Resize 3D object to unit size.
43171     CImg<T> resize_object3d() {
43172       if (_height!=3 || _depth>1 || _spectrum>1)
43173         throw CImgInstanceException(_cimg_instance
43174                                     "resize_object3d(): Instance is not a set of 3D vertices.",
43175                                     cimg_instance);
43176 
43177       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
43178       float
43179         xm, xM = (float)xcoords.max_min(xm),
43180         ym, yM = (float)ycoords.max_min(ym),
43181         zm, zM = (float)zcoords.max_min(zm);
43182       const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
43183       if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
43184       return *this;
43185     }
43186 
43187     //! Resize 3D object to unit size \newinstance.
43188     CImg<Tfloat> get_resize_object3d() const {
43189       return CImg<Tfloat>(*this,false).resize_object3d();
43190     }
43191 
43192     //! Merge two 3D objects together.
43193     /**
43194        \param[in,out] primitives Primitives data of the current 3D object.
43195        \param obj_vertices Vertices data of the additional 3D object.
43196        \param obj_primitives Primitives data of the additional 3D object.
43197     **/
43198     template<typename tf, typename tp, typename tff>
43199     CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices,
43200                              const CImgList<tff>& obj_primitives) {
43201       if (!obj_vertices || !obj_primitives) return *this;
43202       if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
43203         throw CImgInstanceException(_cimg_instance
43204                                     "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a "
43205                                     "set of 3D vertices.",
43206                                     cimg_instance,
43207                                     obj_vertices._width,obj_vertices._height,
43208                                     obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
43209 
43210       if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
43211       if (_height!=3 || _depth>1 || _spectrum>1)
43212         throw CImgInstanceException(_cimg_instance
43213                                     "append_object3d(): Instance is not a set of 3D vertices.",
43214                                     cimg_instance);
43215 
43216       const unsigned int P = _width;
43217       append(obj_vertices,'x');
43218       const unsigned int N = primitives._width;
43219       primitives.insert(obj_primitives);
43220       for (unsigned int i = N; i<primitives._width; ++i) {
43221         CImg<tf> &p = primitives[i];
43222         switch (p.size()) {
43223         case 1 : p[0]+=P; break; // Point
43224         case 5 : p[0]+=P; p[1]+=P; break; // Sphere
43225         case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment
43226         case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle
43227         case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle
43228         }
43229       }
43230       return *this;
43231     }
43232 
43233     //! Texturize primitives of a 3D object.
43234     /**
43235        \param[in,out] primitives Primitives data of the 3D object.
43236        \param[in,out] colors Colors data of the 3D object.
43237        \param texture Texture image to map to 3D object.
43238        \param coords Texture-mapping coordinates.
43239     **/
43240     template<typename tp, typename tc, typename tt, typename tx>
43241     const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
43242                                       const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::const_empty()) const {
43243       if (is_empty()) return *this;
43244       if (_height!=3)
43245         throw CImgInstanceException(_cimg_instance
43246                                     "texturize_object3d(): image instance is not a set of 3D points.",
43247                                     cimg_instance);
43248       if (coords && (coords._width!=_width || coords._height!=2))
43249         throw CImgArgumentException(_cimg_instance
43250                                     "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
43251                                     cimg_instance,
43252                                     coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
43253       CImg<intT> _coords;
43254       if (!coords) { // If no texture coordinates specified, do a default XY-projection
43255         _coords.assign(_width,2);
43256         float
43257           xmin, xmax = (float)get_shared_row(0).max_min(xmin),
43258           ymin, ymax = (float)get_shared_row(1).max_min(ymin),
43259           dx = xmax>xmin?xmax-xmin:1,
43260           dy = ymax>ymin?ymax-ymin:1;
43261         cimg_forX(*this,p) {
43262           _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx);
43263           _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy);
43264         }
43265       } else _coords = coords;
43266 
43267       int texture_ind = -1;
43268       cimglist_for(primitives,l) {
43269         CImg<tp> &p = primitives[l];
43270         const unsigned int siz = p.size();
43271         switch (siz) {
43272         case 1 : { // Point
43273           const unsigned int i0 = (unsigned int)p[0];
43274           const int x0 = _coords(i0,0), y0 = _coords(i0,1);
43275           texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
43276                                 y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]);
43277         } break;
43278         case 2 : case 6 : { // Line
43279           const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1];
43280           const int
43281             x0 = _coords(i0,0), y0 = _coords(i0,1),
43282             x1 = _coords(i1,0), y1 = _coords(i1,1);
43283           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
43284           else colors[l].assign(colors[texture_ind],true);
43285           CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
43286         } break;
43287         case 3 : case 9 : { // Triangle
43288           const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2];
43289           const int
43290             x0 = _coords(i0,0), y0 = _coords(i0,1),
43291             x1 = _coords(i1,0), y1 = _coords(i1,1),
43292             x2 = _coords(i2,0), y2 = _coords(i2,1);
43293           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
43294           else colors[l].assign(colors[texture_ind],true);
43295           CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
43296         } break;
43297         case 4 : case 12 : { // Quadrangle
43298           const unsigned int
43299             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3];
43300           const int
43301             x0 = _coords(i0,0), y0 = _coords(i0,1),
43302             x1 = _coords(i1,0), y1 = _coords(i1,1),
43303             x2 = _coords(i2,0), y2 = _coords(i2,1),
43304             x3 = _coords(i3,0), y3 = _coords(i3,1);
43305           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
43306           else colors[l].assign(colors[texture_ind],true);
43307           CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
43308         } break;
43309         }
43310       }
43311       return *this;
43312     }
43313 
43314     //! Generate a 3D elevation of the image instance.
43315     /**
43316        \param[out] primitives The returned list of the 3D object primitives
43317                               (template type \e tf should be at least \e unsigned \e int).
43318        \param[out] colors The returned list of the 3D object colors.
43319        \param elevation The input elevation map.
43320        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
43321        \par Example
43322        \code
43323        const CImg<float> img("reference.jpg");
43324        CImgList<unsigned int> faces3d;
43325        CImgList<unsigned char> colors3d;
43326        const CImg<float> points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2);
43327        CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
43328        \endcode
43329        \image html ref_elevation3d.jpg
43330     **/
43331     template<typename tf, typename tc, typename te>
43332     CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
43333       if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
43334         throw CImgArgumentException(_cimg_instance
43335                                     "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
43336                                     "have incompatible dimensions.",
43337                                     cimg_instance,
43338                                     elevation._width,elevation._height,elevation._depth,
43339                                     elevation._spectrum,elevation._data);
43340       if (is_empty()) return *this;
43341       float m, M = (float)max_min(m);
43342       if (M==m) ++M;
43343       colors.assign();
43344       const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
43345       for (unsigned int y = 0; y<size_y1; ++y)
43346         for (unsigned int x = 0; x<size_x1; ++x) {
43347           const unsigned char
43348             r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
43349             g = (unsigned char)(_spectrum>1?((*this)(x,y,1) - m)*255/(M-m):r),
43350             b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r);
43351           CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
43352         }
43353       const typename CImg<te>::_functor2d_int func(elevation);
43354       return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height);
43355     }
43356 
43357     //! Generate the 3D projection planes of the image instance.
43358     /**
43359        \param[out] primitives Primitives data of the returned 3D object.
43360        \param[out] colors Colors data of the returned 3D object.
43361        \param x0 X-coordinate of the projection point.
43362        \param y0 Y-coordinate of the projection point.
43363        \param z0 Z-coordinate of the projection point.
43364        \param normalize_colors Tells if the created textures have normalized colors.
43365     **/
43366     template<typename tf, typename tc>
43367     CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
43368                                    const unsigned int x0, const unsigned int y0, const unsigned int z0,
43369                                    const bool normalize_colors=false) const {
43370       float m = 0, M = 0, delta = 1;
43371       if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
43372       const unsigned int
43373         _x0 = (x0>=_width)?_width - 1:x0,
43374         _y0 = (y0>=_height)?_height - 1:y0,
43375         _z0 = (z0>=_depth)?_depth - 1:z0;
43376       CImg<tc> img_xy, img_xz, img_yz;
43377       if (normalize_colors) {
43378         ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy);
43379         ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1).
43380           move_to(img_xz);
43381         ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1).
43382           move_to(img_yz);
43383       } else {
43384         get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy);
43385         get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
43386         get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
43387       }
43388       CImg<floatT> points(12,3,1,1,
43389                           0,_width - 1,_width - 1,0,   0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0,
43390                           0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0,       0,_height - 1,_height - 1,0,
43391                           _z0,_z0,_z0,_z0,         0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1);
43392       primitives.assign();
43393       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).
43394         move_to(primitives);
43395       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).
43396         move_to(primitives);
43397       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).
43398         move_to(primitives);
43399       colors.assign();
43400       img_xy.move_to(colors);
43401       img_xz.move_to(colors);
43402       img_yz.move_to(colors);
43403       return points;
43404     }
43405 
43406     //! Generate a isoline of the image instance as a 3D object.
43407     /**
43408        \param[out] primitives The returned list of the 3D object primitives
43409                               (template type \e tf should be at least \e unsigned \e int).
43410        \param isovalue The returned list of the 3D object colors.
43411        \param size_x The number of subdivisions along the X-axis.
43412        \param size_y The number of subdisivions along the Y-axis.
43413        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
43414        \par Example
43415        \code
43416        const CImg<float> img("reference.jpg");
43417        CImgList<unsigned int> faces3d;
43418        const CImg<float> points3d = img.get_isoline3d(faces3d,100);
43419        CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
43420        \endcode
43421        \image html ref_isoline3d.jpg
43422     **/
43423     template<typename tf>
43424     CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
43425                                const int size_x=-100, const int size_y=-100) const {
43426       if (_spectrum>1)
43427         throw CImgInstanceException(_cimg_instance
43428                                     "get_isoline3d(): Instance is not a scalar image.",
43429                                     cimg_instance);
43430       if (_depth>1)
43431         throw CImgInstanceException(_cimg_instance
43432                                     "get_isoline3d(): Instance is not a 2D image.",
43433                                     cimg_instance);
43434       primitives.assign();
43435       if (is_empty()) return *this;
43436       CImg<floatT> vertices;
43437       if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
43438         const _functor2d_int func(*this);
43439         vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height());
43440       } else {
43441         const _functor2d_float func(*this);
43442         vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y);
43443       }
43444       return vertices;
43445     }
43446 
43447     //! Compute isolines of a function, as a 3D object.
43448     /**
43449        \param[out] primitives Primitives data of the resulting 3D object.
43450        \param func Elevation functor. Must have <tt>operator()(x,y)</tt> defined.
43451        \param isovalue Isovalue to extract from function.
43452        \param x0 X-coordinate of the starting point.
43453        \param y0 Y-coordinate of the starting point.
43454        \param x1 X-coordinate of the ending point.
43455        \param y1 Y-coordinate of the ending point.
43456        \param size_x Resolution of the function along the X-axis.
43457        \param size_y Resolution of the function along the Y-axis.
43458        \note Use the marching squares algorithm for extracting the isolines.
43459      **/
43460     template<typename tf, typename tfunc>
43461     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
43462                                   const float x0, const float y0, const float x1, const float y1,
43463                                   const int size_x=256, const int size_y=256) {
43464       CImgList<floatT> vertices;
43465       primitives.assign();
43466       typename CImg<floatT>::_functor_isoline3d add_vertex(vertices);
43467       typename CImg<tf>::_functor_isoline3d add_segment(primitives);
43468       isoline3d(add_vertex,add_segment,func,isovalue,x0,y0,x1,y1,size_x,size_y);
43469       return vertices>'x';
43470     }
43471 
43472     //! Compute isolines of a function, as a 3D object.
43473     /**
43474        \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
43475        \param[out] add_segment : Functor with operator()(i,j) defined for adding new segment.
43476        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
43477        \param isovalue Isovalue to extract from function.
43478        \param x0 X-coordinate of the starting point.
43479        \param y0 Y-coordinate of the starting point.
43480        \param x1 X-coordinate of the ending point.
43481        \param y1 Y-coordinate of the ending point.
43482        \param size_x Resolution of the function along the X-axis.
43483        \param size_y Resolution of the function along the Y-axis.
43484        \note Use the marching squares algorithm for extracting the isolines.
43485      **/
43486     template<typename tv, typename tf, typename tfunc>
43487     static void isoline3d(tv& add_vertex, tf& add_segment, const tfunc& func, const float isovalue,
43488                           const float x0, const float y0, const float x1, const float y1,
43489                           const int size_x, const int size_y) {
43490       static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc,
43491                                               0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
43492       static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
43493                                            { 1,2,-1,-1 },   { 0,1,2,3 },   { 0,2,-1,-1 }, { 2,3,-1,-1 },
43494                                            { 2,3,-1,-1 },   { 0,2,-1,-1},  { 0,3,1,2 },   { 1,2,-1,-1 },
43495                                            { 1,3,-1,-1 },   { 0,1,-1,-1},  { 0,3,-1,-1},  { -1,-1,-1,-1 } };
43496       const unsigned int
43497         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
43498         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
43499         nx = _nx?_nx:1,
43500         ny = _ny?_ny:1,
43501         nxm1 = nx - 1,
43502         nym1 = ny - 1;
43503 
43504       if (!nxm1 || !nym1) return;
43505       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
43506       CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
43507       CImg<floatT> values1(nx), values2(nx);
43508       float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
43509       int nb_vertices = 0;
43510 
43511       // Fill first line with values
43512       cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
43513 
43514       // Run the marching squares algorithm
43515       for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
43516         X = x0; nX = X + dx;
43517         indices2.fill(-1);
43518         values2(0) = (float)func(X,nY);
43519         for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
43520 
43521           // Determine square configuration
43522           const float
43523             val0 = values1(xi),
43524             val1 = values1(nxi),
43525             val2 = values2(nxi) = (float)func(nX,nY),
43526             val3 = values2(xi);
43527           const unsigned int
43528             configuration = (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) |
43529             (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U),
43530             edge = edges[configuration];
43531 
43532           // Compute intersection vertices
43533           if (edge) {
43534             if ((edge&1) && indices1(xi,0)<0) {
43535               const float Xi = X + (isovalue-val0)*dx/(val1-val0);
43536               indices1(xi,0) = nb_vertices++;
43537               add_vertex(Xi,Y,0.0f);
43538             }
43539             if ((edge&2) && indices1(nxi,1)<0) {
43540               const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
43541               indices1(nxi,1) = nb_vertices++;
43542               add_vertex(nX,Yi,0.0f);
43543             }
43544             if ((edge&4) && indices2(xi,0)<0) {
43545               const float Xi = X + (isovalue-val3)*dx/(val2-val3);
43546               indices2(xi,0) = nb_vertices++;
43547               add_vertex(Xi,nY,0.0f);
43548             }
43549             if ((edge&8) && indices1(xi,1)<0) {
43550               const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
43551               indices1(xi,1) = nb_vertices++;
43552               add_vertex(X,Yi,0.0f);
43553             }
43554 
43555             // Create segments
43556             for (const int *segment = segments[configuration]; *segment!=-1; ) {
43557               const unsigned int p0 = (unsigned int)*(segment++), p1 = (unsigned int)*(segment++);
43558               const int
43559                 i0 = _isoline3d_index(p0,indices1,indices2,xi,nxi),
43560                 i1 = _isoline3d_index(p1,indices1,indices2,xi,nxi);
43561               add_segment(i0,i1);
43562             }
43563           }
43564         }
43565         values1.swap(values2);
43566         indices1.swap(indices2);
43567       }
43568     }
43569 
43570     //! Compute isolines of a function, as a 3D object \overloading.
43571     template<typename tf>
43572     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
43573                                   const float x0, const float y0, const float x1, const float y1,
43574                                   const int size_x=256, const int size_y=256) {
43575       const _functor2d_expr func(expression);
43576       return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
43577     }
43578 
43579     template<typename t>
43580     static int _isoline3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
43581                                  const unsigned int x, const unsigned int nx) {
43582       switch (edge) {
43583       case 0 : return (int)indices1(x,0);
43584       case 1 : return (int)indices1(nx,1);
43585       case 2 : return (int)indices2(x,0);
43586       case 3 : return (int)indices1(x,1);
43587       }
43588       return 0;
43589     }
43590 
43591     //! Generate an isosurface of the image instance as a 3D object.
43592     /**
43593        \param[out] primitives The returned list of the 3D object primitives
43594                               (template type \e tf should be at least \e unsigned \e int).
43595        \param isovalue The returned list of the 3D object colors.
43596        \param size_x Number of subdivisions along the X-axis.
43597        \param size_y Number of subdisivions along the Y-axis.
43598        \param size_z Number of subdisivions along the Z-axis.
43599        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
43600        \par Example
43601        \code
43602        const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
43603        CImgList<unsigned int> faces3d;
43604        const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
43605        CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
43606        \endcode
43607        \image html ref_isosurface3d.jpg
43608     **/
43609     template<typename tf>
43610     CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
43611                                   const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
43612       if (_spectrum>1)
43613         throw CImgInstanceException(_cimg_instance
43614                                     "get_isosurface3d(): Instance is not a scalar image.",
43615                                     cimg_instance);
43616       primitives.assign();
43617       if (is_empty()) return *this;
43618       CImg<floatT> vertices;
43619       if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
43620         const _functor3d_int func(*this);
43621         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
43622                                 width(),height(),depth());
43623       } else {
43624         const _functor3d_float func(*this);
43625         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
43626                                 size_x,size_y,size_z);
43627       }
43628       return vertices;
43629     }
43630 
43631     //! Compute isosurface of a function, as a 3D object.
43632     /**
43633        \param[out] primitives Primitives data of the resulting 3D object.
43634        \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
43635        \param isovalue Isovalue to extract.
43636        \param x0 X-coordinate of the starting point.
43637        \param y0 Y-coordinate of the starting point.
43638        \param z0 Z-coordinate of the starting point.
43639        \param x1 X-coordinate of the ending point.
43640        \param y1 Y-coordinate of the ending point.
43641        \param z1 Z-coordinate of the ending point.
43642        \param size_x Resolution of the elevation function along the X-axis.
43643        \param size_y Resolution of the elevation function along the Y-axis.
43644        \param size_z Resolution of the elevation function along the Z-axis.
43645        \note Use the marching cubes algorithm for extracting the isosurface.
43646      **/
43647     template<typename tf, typename tfunc>
43648     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
43649                                      const float x0, const float y0, const float z0,
43650                                      const float x1, const float y1, const float z1,
43651                                      const int size_x=32, const int size_y=32, const int size_z=32) {
43652       CImgList<floatT> vertices;
43653       primitives.assign();
43654       typename CImg<floatT>::_functor_isosurface3d add_vertex(vertices);
43655       typename CImg<tf>::_functor_isosurface3d add_triangle(primitives);
43656       isosurface3d(add_vertex,add_triangle,func,isovalue,x0,y0,z0,x1,y1,z1,size_x,size_y,size_z);
43657       return vertices>'x';
43658     }
43659 
43660     //! Compute isosurface of a function, as a 3D object.
43661     /**
43662        \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
43663        \param[out] add_triangle : Functor with operator()(i,j) defined for adding new segment.
43664        \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
43665        \param isovalue Isovalue to extract.
43666        \param x0 X-coordinate of the starting point.
43667        \param y0 Y-coordinate of the starting point.
43668        \param z0 Z-coordinate of the starting point.
43669        \param x1 X-coordinate of the ending point.
43670        \param y1 Y-coordinate of the ending point.
43671        \param z1 Z-coordinate of the ending point.
43672        \param size_x Resolution of the elevation function along the X-axis.
43673        \param size_y Resolution of the elevation function along the Y-axis.
43674        \param size_z Resolution of the elevation function along the Z-axis.
43675        \note Use the marching cubes algorithm for extracting the isosurface.
43676      **/
43677     template<typename tv, typename tf, typename tfunc>
43678     static void isosurface3d(tv& add_vertex, tf& add_triangle, const tfunc& func, const float isovalue,
43679                              const float x0, const float y0, const float z0,
43680                              const float x1, const float y1, const float z1,
43681                              const int size_x, const int size_y, const int size_z) {
43682       static const unsigned int edges[256] = {
43683         0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
43684         0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
43685         0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
43686         0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
43687         0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
43688         0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
43689         0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
43690         0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
43691         0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
43692         0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
43693         0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
43694         0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
43695         0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
43696         0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
43697         0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
43698         0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
43699       };
43700 
43701       static const int triangles[256][16] = {
43702         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43703         { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43704         { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43705         { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43706         { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43707         { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43708         { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43709         { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
43710         { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43711         { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43712         { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43713         { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
43714         { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43715         { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
43716         { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
43717         { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43718         { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43719         { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43720         { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43721         { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
43722         { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43723         { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
43724         { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
43725         { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
43726         { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43727         { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
43728         { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
43729         { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
43730         { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 },
43731         { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
43732         { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
43733         { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
43734         { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43735         { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43736         { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43737         { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
43738         { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43739         { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
43740         { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
43741         { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
43742         { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43743         { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
43744         { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
43745         { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
43746         { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 },
43747         { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
43748         { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
43749         { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
43750         { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43751         { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
43752         { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
43753         { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43754         { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 },
43755         { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
43756         { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 },
43757         { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
43758         { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 },
43759         { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
43760         { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 },
43761         { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
43762         { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 },
43763         { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
43764         { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 },
43765         { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43766         { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43767         { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43768         { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43769         { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
43770         { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43771         { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
43772         { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
43773         { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
43774         { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43775         { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
43776         { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
43777         { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
43778         { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
43779         { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
43780         { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 },
43781         { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
43782         { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43783         { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
43784         { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
43785         { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
43786         { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 },
43787         { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
43788         { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 },
43789         { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
43790         { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
43791         { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
43792         { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 },
43793         { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
43794         { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
43795         { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
43796         { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 },
43797         { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
43798         { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43799         { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
43800         { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
43801         { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
43802         { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
43803         { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
43804         { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43805         { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
43806         { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 },
43807         { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
43808         { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
43809         { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
43810         { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 },
43811         { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
43812         { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
43813         { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43814         { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
43815         { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
43816         { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 },
43817         { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
43818         { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
43819         { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
43820         { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
43821         { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43822         { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
43823         { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
43824         { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 },
43825         { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
43826         { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 },
43827         { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43828         { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 },
43829         { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43830         { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43831         { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43832         { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43833         { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
43834         { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43835         { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
43836         { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
43837         { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
43838         { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43839         { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
43840         { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 },
43841         { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
43842         { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
43843         { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
43844         { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 },
43845         { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
43846         { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43847         { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
43848         { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 },
43849         { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
43850         { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 },
43851         { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
43852         { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 },
43853         { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
43854         { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
43855         { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43856         { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 },
43857         { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
43858         { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 },
43859         { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
43860         { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 },
43861         { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43862         { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43863         { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
43864         { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
43865         { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
43866         { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
43867         { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
43868         { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 },
43869         { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
43870         { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 },
43871         { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
43872         { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 },
43873         { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
43874         { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 },
43875         { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
43876         { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 },
43877         { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
43878         { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
43879         { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
43880         { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 },
43881         { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
43882         { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 },
43883         { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
43884         { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 },
43885         { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
43886         { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 },
43887         { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
43888         { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 },
43889         { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43890         { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 },
43891         { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
43892         { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43893         { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43894         { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43895         { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
43896         { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 },
43897         { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
43898         { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
43899         { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
43900         { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 },
43901         { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
43902         { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
43903         { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
43904         { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 },
43905         { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
43906         { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43907         { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
43908         { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
43909         { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43910         { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
43911         { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
43912         { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 },
43913         { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
43914         { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 },
43915         { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
43916         { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 },
43917         { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43918         { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 },
43919         { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
43920         { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 },
43921         { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
43922         { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
43923         { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43924         { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 },
43925         { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43926         { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
43927         { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
43928         { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 },
43929         { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
43930         { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 },
43931         { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
43932         { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
43933         { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
43934         { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 },
43935         { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
43936         { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 },
43937         { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43938         { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
43939         { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
43940         { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43941         { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43942         { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43943         { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
43944         { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
43945         { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43946         { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
43947         { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
43948         { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43949         { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43950         { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
43951         { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43952         { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 },
43953         { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43954         { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43955         { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43956         { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
43957         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
43958       };
43959 
43960       const unsigned int
43961         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
43962         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
43963         _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
43964         nx = _nx?_nx:1,
43965         ny = _ny?_ny:1,
43966         nz = _nz?_nz:1,
43967         nxm1 = nx - 1,
43968         nym1 = ny - 1,
43969         nzm1 = nz - 1;
43970       if (!nxm1 || !nym1 || !nzm1) return;
43971       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
43972       CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
43973       CImg<floatT> values1(nx,ny), values2(nx,ny);
43974       float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
43975       int nb_vertices = 0;
43976 
43977       // Fill the first plane with function values
43978       Y = y0;
43979       cimg_forY(values1,y) {
43980         X = x0;
43981         cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
43982         Y+=dy;
43983       }
43984 
43985       // Run Marching Cubes algorithm
43986       Z = z0; nZ = Z + dz;
43987       for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
43988         Y = y0; nY = Y + dy;
43989         indices2.fill(-1);
43990         X = x0; for (unsigned int xi = 0; xi<nx; ++xi) { values2(xi,0) = (float)func(X,Y,nZ); X += dx; }
43991 
43992         for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
43993           X = x0; nX = X + dx;
43994           values2(0,nyi) = (float)func(X,nY,nZ);
43995 
43996           for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
43997 
43998             // Determine cube configuration
43999             const float
44000               val0 = values1(xi,yi),
44001               val1 = values1(nxi,yi),
44002               val2 = values1(nxi,nyi),
44003               val3 = values1(xi,nyi),
44004               val4 = values2(xi,yi),
44005               val5 = values2(nxi,yi),
44006               val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
44007               val7 = values2(xi,nyi);
44008 
44009             const unsigned int configuration =
44010               (val0<isovalue?1U:0U)  | (val1<isovalue?2U:0U)  | (val2<isovalue?4U:0U)  | (val3<isovalue?8U:0U) |
44011               (val4<isovalue?16U:0U) | (val5<isovalue?32U:0U) | (val6<isovalue?64U:0U) | (val7<isovalue?128U:0U),
44012               edge = edges[configuration];
44013 
44014             // Compute intersection vertices
44015             if (edge) {
44016               if ((edge&1) && indices1(xi,yi,0)<0) {
44017                 const float Xi = X + (isovalue-val0)*dx/(val1-val0);
44018                 indices1(xi,yi,0) = nb_vertices++;
44019                 add_vertex(Xi,Y,Z);
44020               }
44021               if ((edge&2) && indices1(nxi,yi,1)<0) {
44022                 const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
44023                 indices1(nxi,yi,1) = nb_vertices++;
44024                 add_vertex(nX,Yi,Z);
44025               }
44026               if ((edge&4) && indices1(xi,nyi,0)<0) {
44027                 const float Xi = X + (isovalue-val3)*dx/(val2-val3);
44028                 indices1(xi,nyi,0) = nb_vertices++;
44029                 add_vertex(Xi,nY,Z);
44030               }
44031               if ((edge&8) && indices1(xi,yi,1)<0) {
44032                 const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
44033                 indices1(xi,yi,1) = nb_vertices++;
44034                 add_vertex(X,Yi,Z);
44035               }
44036               if ((edge&16) && indices2(xi,yi,0)<0) {
44037                 const float Xi = X + (isovalue-val4)*dx/(val5-val4);
44038                 indices2(xi,yi,0) = nb_vertices++;
44039                 add_vertex(Xi,Y,nZ);
44040               }
44041               if ((edge&32) && indices2(nxi,yi,1)<0) {
44042                 const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
44043                 indices2(nxi,yi,1) = nb_vertices++;
44044                 add_vertex(nX,Yi,nZ);
44045               }
44046               if ((edge&64) && indices2(xi,nyi,0)<0) {
44047                 const float Xi = X + (isovalue-val7)*dx/(val6-val7);
44048                 indices2(xi,nyi,0) = nb_vertices++;
44049                 add_vertex(Xi,nY,nZ);
44050               }
44051               if ((edge&128) && indices2(xi,yi,1)<0)  {
44052                 const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
44053                 indices2(xi,yi,1) = nb_vertices++;
44054                 add_vertex(X,Yi,nZ);
44055               }
44056               if ((edge&256) && indices1(xi,yi,2)<0) {
44057                 const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
44058                 indices1(xi,yi,2) = nb_vertices++;
44059                 add_vertex(X,Y,Zi);
44060               }
44061               if ((edge&512) && indices1(nxi,yi,2)<0)  {
44062                 const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
44063                 indices1(nxi,yi,2) = nb_vertices++;
44064                 add_vertex(nX,Y,Zi);
44065               }
44066               if ((edge&1024) && indices1(nxi,nyi,2)<0) {
44067                 const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
44068                 indices1(nxi,nyi,2) = nb_vertices++;
44069                 add_vertex(nX,nY,Zi);
44070               }
44071               if ((edge&2048) && indices1(xi,nyi,2)<0) {
44072                 const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
44073                 indices1(xi,nyi,2) = nb_vertices++;
44074                 add_vertex(X,nY,Zi);
44075               }
44076 
44077               // Create triangles
44078               for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
44079                 const unsigned int
44080                   p0 = (unsigned int)*(triangle++),
44081                   p1 = (unsigned int)*(triangle++),
44082                   p2 = (unsigned int)*(triangle++);
44083                 const int
44084                   i0 = _isosurface3d_index(p0,indices1,indices2,xi,yi,nxi,nyi),
44085                   i1 = _isosurface3d_index(p1,indices1,indices2,xi,yi,nxi,nyi),
44086                   i2 = _isosurface3d_index(p2,indices1,indices2,xi,yi,nxi,nyi);
44087                 add_triangle(i0,i2,i1);
44088               }
44089             }
44090           }
44091         }
44092         cimg::swap(values1,values2);
44093         cimg::swap(indices1,indices2);
44094       }
44095     }
44096 
44097     //! Compute isosurface of a function, as a 3D object \overloading.
44098     template<typename tf>
44099     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
44100                                      const float x0, const float y0, const float z0,
44101                                      const float x1, const float y1, const float z1,
44102                                      const int dx=32, const int dy=32, const int dz=32) {
44103       const _functor3d_expr func(expression);
44104       return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
44105     }
44106 
44107     template<typename t>
44108     static int _isosurface3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
44109                                     const unsigned int x, const unsigned int y,
44110                                     const unsigned int nx, const unsigned int ny) {
44111       switch (edge) {
44112       case 0 : return indices1(x,y,0);
44113       case 1 : return indices1(nx,y,1);
44114       case 2 : return indices1(x,ny,0);
44115       case 3 : return indices1(x,y,1);
44116       case 4 : return indices2(x,y,0);
44117       case 5 : return indices2(nx,y,1);
44118       case 6 : return indices2(x,ny,0);
44119       case 7 : return indices2(x,y,1);
44120       case 8 : return indices1(x,y,2);
44121       case 9 : return indices1(nx,y,2);
44122       case 10 : return indices1(nx,ny,2);
44123       case 11 : return indices1(x,ny,2);
44124       }
44125       return 0;
44126     }
44127 
44128     // Define functors for accessing image values (used in previous functions).
44129     struct _functor2d_int {
44130       const CImg<T>& ref;
44131       _functor2d_int(const CImg<T>& pref):ref(pref) {}
44132       float operator()(const float x, const float y) const {
44133         return (float)ref((int)x,(int)y);
44134       }
44135     };
44136 
44137     struct _functor2d_float {
44138       const CImg<T>& ref;
44139       _functor2d_float(const CImg<T>& pref):ref(pref) {}
44140       float operator()(const float x, const float y) const {
44141         return (float)ref._linear_atXY(x,y);
44142       }
44143     };
44144 
44145     struct _functor2d_expr {
44146       _cimg_math_parser *mp;
44147       ~_functor2d_expr() { mp->end(); delete mp; }
44148       _functor2d_expr(const char *const expr):mp(0) {
44149         mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
44150       }
44151       float operator()(const float x, const float y) const {
44152         return (float)(*mp)(x,y,0,0);
44153       }
44154     };
44155 
44156     struct _functor3d_int {
44157       const CImg<T>& ref;
44158       _functor3d_int(const CImg<T>& pref):ref(pref) {}
44159       float operator()(const float x, const float y, const float z) const {
44160         return (float)ref((int)x,(int)y,(int)z);
44161       }
44162     };
44163 
44164     struct _functor3d_float {
44165       const CImg<T>& ref;
44166       _functor3d_float(const CImg<T>& pref):ref(pref) {}
44167       float operator()(const float x, const float y, const float z) const {
44168         return (float)ref._linear_atXYZ(x,y,z);
44169       }
44170     };
44171 
44172     struct _functor3d_expr {
44173       _cimg_math_parser *mp;
44174       ~_functor3d_expr() { mp->end(); delete mp; }
44175       _functor3d_expr(const char *const expr):mp(0) {
44176         mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
44177       }
44178       float operator()(const float x, const float y, const float z) const {
44179         return (float)(*mp)(x,y,z,0);
44180       }
44181     };
44182 
44183     struct _functor4d_int {
44184       const CImg<T>& ref;
44185       _functor4d_int(const CImg<T>& pref):ref(pref) {}
44186       float operator()(const float x, const float y, const float z, const unsigned int c) const {
44187         return (float)ref((int)x,(int)y,(int)z,c);
44188       }
44189     };
44190 
44191     struct _functor_isoline3d {
44192       CImgList<T>& list;
44193       _functor_isoline3d(CImgList<T>& _list):list(_list) {}
44194       template<typename t>
44195       void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
44196       template<typename t>
44197       void operator()(const t i, const t j) { CImg<T>::vector((T)i,(T)j).move_to(list); }
44198     };
44199 
44200     struct _functor_isosurface3d {
44201       CImgList<T>& list;
44202       _functor_isosurface3d(CImgList<T>& _list):list(_list) {}
44203       template<typename t>
44204       void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
44205     };
44206 
44207     //! Compute 3D elevation of a function as a 3D object.
44208     /**
44209        \param[out] primitives Primitives data of the resulting 3D object.
44210        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
44211        \param x0 X-coordinate of the starting point.
44212        \param y0 Y-coordinate of the starting point.
44213        \param x1 X-coordinate of the ending point.
44214        \param y1 Y-coordinate of the ending point.
44215        \param size_x Resolution of the function along the X-axis.
44216        \param size_y Resolution of the function along the Y-axis.
44217     **/
44218     template<typename tf, typename tfunc>
44219     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
44220                                     const float x0, const float y0, const float x1, const float y1,
44221                                     const int size_x=256, const int size_y=256) {
44222       const float
44223         nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
44224         nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
44225       const unsigned int
44226         _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100),
44227         nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
44228         _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100),
44229         nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
44230       if (nsize_x<2 || nsize_y<2)
44231         throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
44232                                     pixel_type(),
44233                                     nsize_x,nsize_y);
44234 
44235       CImg<floatT> vertices(nsize_x*nsize_y,3);
44236       floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
44237       for (unsigned int y = 0; y<nsize_y; ++y) {
44238         const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
44239         for (unsigned int x = 0; x<nsize_x; ++x) {
44240           const float X = nx0 + x*(nx1-nx0)/nsize_x1;
44241           *(ptr_x++) = (float)x;
44242           *(ptr_y++) = (float)y;
44243           *(ptr_z++) = (float)func(X,Y);
44244         }
44245       }
44246       primitives.assign(nsize_x1*nsize_y1,1,4);
44247       for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
44248         const unsigned int yw = y*nsize_x;
44249         for (unsigned int x = 0; x<nsize_x1; ++x) {
44250           const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
44251           primitives[p++].fill(xpyw,xpyww,xpyww + 1,xpyw + 1);
44252         }
44253       }
44254       return vertices;
44255     }
44256 
44257     //! Compute 3D elevation of a function, as a 3D object \overloading.
44258     template<typename tf>
44259     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
44260                                     const float x0, const float y0, const float x1, const float y1,
44261                                     const int size_x=256, const int size_y=256) {
44262       const _functor2d_expr func(expression);
44263       return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
44264     }
44265 
44266     //! Generate a 3D box object.
44267     /**
44268        \param[out] primitives The returned list of the 3D object primitives
44269                               (template type \e tf should be at least \e unsigned \e int).
44270        \param size_x The width of the box (dimension along the X-axis).
44271        \param size_y The height of the box (dimension along the Y-axis).
44272        \param size_z The depth of the box (dimension along the Z-axis).
44273        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44274        \par Example
44275        \code
44276        CImgList<unsigned int> faces3d;
44277        const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
44278        CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
44279        \endcode
44280        \image html ref_box3d.jpg
44281     **/
44282     template<typename tf>
44283     static CImg<floatT> box3d(CImgList<tf>& primitives,
44284                               const float size_x=200, const float size_y=100, const float size_z=100) {
44285       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);
44286       return CImg<floatT>(8,3,1,1,
44287                           0.,size_x,size_x,    0.,    0.,size_x,size_x,    0.,
44288                           0.,    0.,size_y,size_y,    0.,    0.,size_y,size_y,
44289                           0.,    0.,    0.,    0.,size_z,size_z,size_z,size_z);
44290     }
44291 
44292     //! Generate a 3D cone.
44293     /**
44294        \param[out] primitives The returned list of the 3D object primitives
44295                               (template type \e tf should be at least \e unsigned \e int).
44296        \param radius The radius of the cone basis.
44297        \param size_z The cone's height.
44298        \param subdivisions The number of basis angular subdivisions.
44299        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44300        \par Example
44301        \code
44302        CImgList<unsigned int> faces3d;
44303        const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
44304        CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
44305        \endcode
44306        \image html ref_cone3d.jpg
44307     **/
44308     template<typename tf>
44309     static CImg<floatT> cone3d(CImgList<tf>& primitives,
44310                                const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
44311       primitives.assign();
44312       if (!subdivisions) return CImg<floatT>();
44313       CImgList<floatT> vertices(2,1,3,1,1,
44314                                 0.,0.,size_z,
44315                                 0.,0.,0.);
44316       for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
44317         const float a = (float)(angle*cimg::PI/180);
44318         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
44319       }
44320       const unsigned int nbr = vertices._width - 2;
44321       for (unsigned int p = 0; p<nbr; ++p) {
44322         const unsigned int curr = 2 + p, next = 2 + ((p + 1)%nbr);
44323         CImg<tf>::vector(1,next,curr).move_to(primitives);
44324         CImg<tf>::vector(0,curr,next).move_to(primitives);
44325       }
44326       return vertices>'x';
44327     }
44328 
44329     //! Generate a 3D cylinder.
44330     /**
44331        \param[out] primitives The returned list of the 3D object primitives
44332                               (template type \e tf should be at least \e unsigned \e int).
44333        \param radius The radius of the cylinder basis.
44334        \param size_z The cylinder's height.
44335        \param subdivisions The number of basis angular subdivisions.
44336        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44337        \par Example
44338        \code
44339        CImgList<unsigned int> faces3d;
44340        const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
44341        CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
44342        \endcode
44343        \image html ref_cylinder3d.jpg
44344     **/
44345     template<typename tf>
44346     static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
44347                                    const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
44348       primitives.assign();
44349       if (!subdivisions) return CImg<floatT>();
44350       CImgList<floatT> vertices(2,1,3,1,1,
44351                                 0.,0.,0.,
44352                                 0.,0.,size_z);
44353       for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
44354         const float a = (float)(angle*cimg::PI/180);
44355         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices);
44356         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
44357       }
44358       const unsigned int nbr = (vertices._width - 2)/2;
44359       for (unsigned int p = 0; p<nbr; ++p) {
44360         const unsigned int curr = 2 + 2*p, next = 2 + (2*((p + 1)%nbr));
44361         CImg<tf>::vector(0,next,curr).move_to(primitives);
44362         CImg<tf>::vector(1,curr + 1,next + 1).move_to(primitives);
44363         CImg<tf>::vector(curr,next,next + 1,curr + 1).move_to(primitives);
44364       }
44365       return vertices>'x';
44366     }
44367 
44368     //! Generate a 3D torus.
44369     /**
44370        \param[out] primitives The returned list of the 3D object primitives
44371                               (template type \e tf should be at least \e unsigned \e int).
44372        \param radius1 The large radius.
44373        \param radius2 The small radius.
44374        \param subdivisions1 The number of angular subdivisions for the large radius.
44375        \param subdivisions2 The number of angular subdivisions for the small radius.
44376        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44377        \par Example
44378        \code
44379        CImgList<unsigned int> faces3d;
44380        const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
44381        CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
44382        \endcode
44383        \image html ref_torus3d.jpg
44384     **/
44385     template<typename tf>
44386     static CImg<floatT> torus3d(CImgList<tf>& primitives,
44387                                 const float radius1=100, const float radius2=30,
44388                                 const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
44389       primitives.assign();
44390       if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
44391       CImgList<floatT> vertices;
44392       for (unsigned int v = 0; v<subdivisions1; ++v) {
44393         const float
44394           beta = (float)(v*2*cimg::PI/subdivisions1),
44395           xc = radius1*(float)std::cos(beta),
44396           yc = radius1*(float)std::sin(beta);
44397         for (unsigned int u = 0; u<subdivisions2; ++u) {
44398           const float
44399             alpha = (float)(u*2*cimg::PI/subdivisions2),
44400             x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
44401             y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
44402             z = radius2*(float)std::sin(alpha);
44403           CImg<floatT>::vector(x,y,z).move_to(vertices);
44404         }
44405       }
44406       for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
44407         const unsigned int nv = (vv + 1)%subdivisions1;
44408         for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
44409           const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
44410           CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives);
44411         }
44412       }
44413       return vertices>'x';
44414     }
44415 
44416     //! Generate a 3D XY-plane.
44417     /**
44418        \param[out] primitives The returned list of the 3D object primitives
44419                               (template type \e tf should be at least \e unsigned \e int).
44420        \param size_x The width of the plane (dimension along the X-axis).
44421        \param size_y The height of the plane (dimensions along the Y-axis).
44422        \param subdivisions_x The number of planar subdivisions along the X-axis.
44423        \param subdivisions_y The number of planar subdivisions along the Y-axis.
44424        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44425        \par Example
44426        \code
44427        CImgList<unsigned int> faces3d;
44428        const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
44429        CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
44430        \endcode
44431        \image html ref_plane3d.jpg
44432     **/
44433     template<typename tf>
44434     static CImg<floatT> plane3d(CImgList<tf>& primitives,
44435                                 const float size_x=100, const float size_y=100,
44436                                 const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
44437       primitives.assign();
44438       if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
44439       CImgList<floatT> vertices;
44440       const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
44441       const float fx = (float)size_x/w, fy = (float)size_y/h;
44442       for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
44443         CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
44444       for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
44445         const int off1 = x + y*w, off2 = x + 1 + y*w, off3 = x + 1 + (y + 1)*w, off4 = x + (y + 1)*w;
44446         CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
44447       }
44448       return vertices>'x';
44449     }
44450 
44451     //! Generate a 3D sphere.
44452     /**
44453        \param[out] primitives The returned list of the 3D object primitives
44454                               (template type \e tf should be at least \e unsigned \e int).
44455        \param radius The radius of the sphere (dimension along the X-axis).
44456        \param subdivisions The number of recursive subdivisions from an initial icosahedron.
44457        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44458        \par Example
44459        \code
44460        CImgList<unsigned int> faces3d;
44461        const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
44462        CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
44463        \endcode
44464        \image html ref_sphere3d.jpg
44465     **/
44466     template<typename tf>
44467     static CImg<floatT> sphere3d(CImgList<tf>& primitives,
44468                                  const float radius=50, const unsigned int subdivisions=3) {
44469 
44470       // Create initial icosahedron
44471       primitives.assign();
44472       const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a;
44473       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,
44474                                 -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a);
44475       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,
44476                         8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
44477                         5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
44478       // edge - length/2
44479       float he = (float)a;
44480 
44481       // Recurse subdivisions
44482       for (unsigned int i = 0; i<subdivisions; ++i) {
44483         const unsigned int L = primitives._width;
44484         he/=2;
44485         const float he2 = he*he;
44486         for (unsigned int l = 0; l<L; ++l) {
44487           const unsigned int
44488             p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
44489           const float
44490             x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
44491             x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
44492             x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
44493             tnx0 = (x0 + x1)/2, tny0 = (y0 + y1)/2, tnz0 = (z0 + z1)/2,
44494             nn0 = cimg::hypot(tnx0,tny0,tnz0),
44495             tnx1 = (x0 + x2)/2, tny1 = (y0 + y2)/2, tnz1 = (z0 + z2)/2,
44496             nn1 = cimg::hypot(tnx1,tny1,tnz1),
44497             tnx2 = (x1 + x2)/2, tny2 = (y1 + y2)/2, tnz2 = (z1 + z2)/2,
44498             nn2 = cimg::hypot(tnx2,tny2,tnz2),
44499             nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
44500             nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
44501             nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
44502           int i0 = -1, i1 = -1, i2 = -1;
44503           cimglist_for(vertices,p) {
44504             const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
44505             if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
44506             if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
44507             if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
44508           }
44509           if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; }
44510           if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; }
44511           if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; }
44512           primitives.remove(0);
44513           CImg<tf>::vector(p0,i0,i1).move_to(primitives);
44514           CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
44515           CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
44516           CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
44517         }
44518       }
44519       return (vertices>'x')*=radius;
44520     }
44521 
44522     //! Generate a 3D ellipsoid.
44523     /**
44524        \param[out] primitives The returned list of the 3D object primitives
44525                               (template type \e tf should be at least \e unsigned \e int).
44526        \param tensor The tensor which gives the shape and size of the ellipsoid.
44527        \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
44528        \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
44529        \par Example
44530        \code
44531        CImgList<unsigned int> faces3d;
44532        const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
44533                          points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
44534        CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
44535        \endcode
44536        \image html ref_ellipsoid3d.jpg
44537     **/
44538     template<typename tf, typename t>
44539     static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
44540                                     const CImg<t>& tensor, const unsigned int subdivisions=3) {
44541       primitives.assign();
44542       if (!subdivisions) return CImg<floatT>();
44543       CImg<floatT> S, V;
44544       tensor.symmetric_eigen(S,V);
44545       const float orient =
44546         (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
44547         (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
44548         (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
44549       if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
44550       const float l0 = S[0], l1 = S[1], l2 = S[2];
44551       CImg<floatT> vertices = sphere3d(primitives,1.,subdivisions);
44552       vertices.get_shared_row(0)*=l0;
44553       vertices.get_shared_row(1)*=l1;
44554       vertices.get_shared_row(2)*=l2;
44555       return V*vertices;
44556     }
44557 
44558     //! Convert 3D object into a CImg3d representation.
44559     /**
44560        \param primitives Primitives data of the 3D object.
44561        \param colors Colors data of the 3D object.
44562        \param opacities Opacities data of the 3D object.
44563        \param full_check Tells if full checking of the 3D object must be performed.
44564     **/
44565     template<typename tp, typename tc, typename to>
44566     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
44567                               const CImgList<tc>& colors,
44568                               const to& opacities,
44569                               const bool full_check=true) {
44570       return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this);
44571     }
44572 
44573     //! Convert 3D object into a CImg3d representation \overloading.
44574     template<typename tp, typename tc>
44575     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
44576                               const CImgList<tc>& colors,
44577                               const bool full_check=true) {
44578       return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this);
44579     }
44580 
44581     //! Convert 3D object into a CImg3d representation \overloading.
44582     template<typename tp>
44583     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
44584                               const bool full_check=true) {
44585       return get_object3dtoCImg3d(primitives,full_check).move_to(*this);
44586     }
44587 
44588     //! Convert 3D object into a CImg3d representation \overloading.
44589     CImg<T>& object3dtoCImg3d(const bool full_check=true) {
44590       return get_object3dtoCImg3d(full_check).move_to(*this);
44591     }
44592 
44593     //! Convert 3D object into a CImg3d representation \newinstance.
44594     template<typename tp, typename tc, typename to>
44595     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
44596                                       const CImgList<tc>& colors,
44597                                       const to& opacities,
44598                                       const bool full_check=true) const {
44599       CImg<charT> error_message(1024);
44600       if (!is_object3d(primitives,colors,opacities,full_check,error_message))
44601         throw CImgInstanceException(_cimg_instance
44602                                     "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).",
44603                                     cimg_instance,_width,primitives._width,error_message.data());
44604       CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
44605       float *ptrd = res._data;
44606 
44607       // Put magick number.
44608       *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
44609       *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
44610 
44611       // Put number of vertices and primitives.
44612       *(ptrd++) = cimg::uint2float(_width);
44613       *(ptrd++) = cimg::uint2float(primitives._width);
44614 
44615       // Put vertex data.
44616       if (is_empty() || !primitives) return res;
44617       const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
44618       cimg_forX(*this,p) {
44619         *(ptrd++) = (float)*(ptrx++);
44620         *(ptrd++) = (float)*(ptry++);
44621         *(ptrd++) = (float)*(ptrz++);
44622       }
44623 
44624       // Put primitive data.
44625       cimglist_for(primitives,p) {
44626         *(ptrd++) = (float)primitives[p].size();
44627         const tp *ptrp = primitives[p]._data;
44628         cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
44629       }
44630 
44631       // Put color/texture data.
44632       const unsigned int csiz = std::min(colors._width,primitives._width);
44633       for (int c = 0; c<(int)csiz; ++c) {
44634         const CImg<tc>& color = colors[c];
44635         const tc *ptrc = color._data;
44636         if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
44637         else {
44638           *(ptrd++) = -128.f;
44639           int shared_ind = -1;
44640           if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
44641           if (shared_ind<0) {
44642             *(ptrd++) = (float)color._width;
44643             *(ptrd++) = (float)color._height;
44644             *(ptrd++) = (float)color._spectrum;
44645             cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
44646           } else {
44647             *(ptrd++) = (float)shared_ind;
44648             *(ptrd++) = 0;
44649             *(ptrd++) = 0;
44650           }
44651         }
44652       }
44653       const int csiz2 = primitives.width() - colors.width();
44654       for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.f; *(ptrd++) = 200.f; *(ptrd++) = 200.f; }
44655 
44656       // Put opacity data.
44657       ptrd = _object3dtoCImg3d(opacities,ptrd);
44658       const float *ptre = res.end();
44659       while (ptrd<ptre) *(ptrd++) = 1.f;
44660       return res;
44661     }
44662 
44663     template<typename to>
44664     float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
44665       cimglist_for(opacities,o) {
44666         const CImg<to>& opacity = opacities[o];
44667         const to *ptro = opacity._data;
44668         if (opacity.size()==1) *(ptrd++) = (float)*ptro;
44669         else {
44670           *(ptrd++) = -128.f;
44671           int shared_ind = -1;
44672           if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
44673           if (shared_ind<0) {
44674             *(ptrd++) = (float)opacity._width;
44675             *(ptrd++) = (float)opacity._height;
44676             *(ptrd++) = (float)opacity._spectrum;
44677             cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
44678           } else {
44679             *(ptrd++) = (float)shared_ind;
44680             *(ptrd++) = 0;
44681             *(ptrd++) = 0;
44682           }
44683         }
44684       }
44685       return ptrd;
44686     }
44687 
44688     template<typename to>
44689     float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
44690       const to *ptro = opacities._data;
44691       cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
44692       return ptrd;
44693     }
44694 
44695     template<typename tp, typename tc, typename to>
44696     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
44697                                         const CImgList<tc>& colors,
44698                                         const CImgList<to>& opacities) const {
44699       unsigned int siz = 8U + 3*_width;
44700       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
44701       for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
44702         if (colors[c].is_shared()) siz+=4;
44703         else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; }
44704       }
44705       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
44706       cimglist_for(opacities,o) {
44707         if (opacities[o].is_shared()) siz+=4;
44708         else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4 + osiz:1; }
44709       }
44710       siz+=primitives._width - opacities._width;
44711       return siz;
44712     }
44713 
44714     template<typename tp, typename tc, typename to>
44715     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
44716                                         const CImgList<tc>& colors,
44717                                         const CImg<to>& opacities) const {
44718       unsigned int siz = 8U + 3*_width;
44719       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
44720       for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
44721         const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3;
44722       }
44723       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
44724       siz+=primitives.size();
44725       cimg::unused(opacities);
44726       return siz;
44727     }
44728 
44729     //! Convert 3D object into a CImg3d representation \overloading.
44730     template<typename tp, typename tc>
44731     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
44732                                       const CImgList<tc>& colors,
44733                                       const bool full_check=true) const {
44734       CImgList<T> opacities;
44735       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
44736     }
44737 
44738     //! Convert 3D object into a CImg3d representation \overloading.
44739     template<typename tp>
44740     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
44741                                       const bool full_check=true) const {
44742       CImgList<T> colors, opacities;
44743       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
44744     }
44745 
44746     //! Convert 3D object into a CImg3d representation \overloading.
44747     CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const {
44748       CImgList<T> opacities, colors;
44749       CImgList<uintT> primitives(width(),1,1,1,1);
44750       cimglist_for(primitives,p) primitives(p,0) = p;
44751       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
44752     }
44753 
44754     //! Convert CImg3d representation into a 3D object.
44755     /**
44756        \param[out] primitives Primitives data of the 3D object.
44757        \param[out] colors Colors data of the 3D object.
44758        \param[out] opacities Opacities data of the 3D object.
44759        \param full_check Tells if full checking of the 3D object must be performed.
44760     **/
44761     template<typename tp, typename tc, typename to>
44762     CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives,
44763                               CImgList<tc>& colors,
44764                               CImgList<to>& opacities,
44765                               const bool full_check=true) {
44766       return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this);
44767     }
44768 
44769     //! Convert CImg3d representation into a 3D object \newinstance.
44770     template<typename tp, typename tc, typename to>
44771     CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives,
44772                                  CImgList<tc>& colors,
44773                                  CImgList<to>& opacities,
44774                                  const bool full_check=true) const {
44775       CImg<charT> error_message(1024);
44776       if (!is_CImg3d(full_check,error_message))
44777         throw CImgInstanceException(_cimg_instance
44778                                     "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
44779                                     cimg_instance,error_message.data());
44780       const T *ptrs = _data + 6;
44781       const unsigned int
44782         nb_points = cimg::float2uint((float)*(ptrs++)),
44783         nb_primitives = cimg::float2uint((float)*(ptrs++));
44784       const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
44785       ptrs+=3*nb_points;
44786       primitives.assign(nb_primitives);
44787       cimglist_for(primitives,p) {
44788         const unsigned int nb_inds = (unsigned int)*(ptrs++);
44789         primitives[p].assign(1,nb_inds);
44790         tp *ptrp = primitives[p]._data;
44791         for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
44792       }
44793       colors.assign(nb_primitives);
44794       cimglist_for(colors,c) {
44795         if (*ptrs==(T)-128) {
44796           ++ptrs;
44797           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
44798           if (!h && !s) colors[c].assign(colors[w],true);
44799           else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
44800         } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
44801       }
44802       opacities.assign(nb_primitives);
44803       cimglist_for(opacities,o) {
44804         if (*ptrs==(T)-128) {
44805           ++ptrs;
44806           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
44807           if (!h && !s) opacities[o].assign(opacities[w],true);
44808           else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
44809         } else opacities[o].assign(1,1,1,1,*(ptrs++));
44810       }
44811       return points;
44812     }
44813 
44814     //@}
44815     //---------------------------
44816     //
44817     //! \name Drawing Functions
44818     //@{
44819     //---------------------------
44820 
44821 #define cimg_init_scanline(opacity) \
44822     static const T _sc_maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); \
44823     const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \
44824     const ulongT _sc_whd = (ulongT)_width*_height*_depth; \
44825     cimg::unused(_sc_maxval);
44826 
44827 #define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \
44828     _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd,_sc_maxval)
44829 
44830     // [internal] The following _draw_scanline() routines are *non user-friendly functions*,
44831     // used only for internal purpose.
44832     // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid.
44833     template<typename tc>
44834     CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
44835                             const tc *const color, const float opacity,
44836                             const float brightness,
44837                             const float nopacity, const float copacity, const ulongT whd, const T _sc_maxval) {
44838       const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width() - 1, dx = nx1 - nx0;
44839       if (dx>=0) {
44840         const tc *col = color;
44841         const ulongT off = whd - dx - 1;
44842         T *ptrd = data(nx0,y);
44843         if (opacity>=1) { // ** Opaque drawing **
44844           if (brightness==1) { // Brightness==1
44845             if (sizeof(T)!=1) cimg_forC(*this,c) {
44846                 const T val = (T)*(col++);
44847                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
44848                 ptrd+=off;
44849               } else cimg_forC(*this,c) {
44850                 const T val = (T)*(col++);
44851                 std::memset(ptrd,(int)val,dx + 1);
44852                 ptrd+=whd;
44853               }
44854           } else if (brightness<1) { // Brightness<1
44855             if (sizeof(T)!=1) cimg_forC(*this,c) {
44856                 const T val = (T)(*(col++)*brightness);
44857                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
44858                 ptrd+=off;
44859               } else cimg_forC(*this,c) {
44860                 const T val = (T)(*(col++)*brightness);
44861                 std::memset(ptrd,(int)val,dx + 1);
44862                 ptrd+=whd;
44863               }
44864           } else { // Brightness>1
44865             if (sizeof(T)!=1) cimg_forC(*this,c) {
44866                 const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
44867                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
44868                 ptrd+=off;
44869               } else cimg_forC(*this,c) {
44870                 const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
44871                 std::memset(ptrd,(int)val,dx + 1);
44872                 ptrd+=whd;
44873               }
44874           }
44875         } else { // ** Transparent drawing **
44876           if (brightness==1) { // Brightness==1
44877             cimg_forC(*this,c) {
44878               const Tfloat val = *(col++)*nopacity;
44879               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
44880               ptrd+=off;
44881             }
44882           } else if (brightness<=1) { // Brightness<1
44883             cimg_forC(*this,c) {
44884               const Tfloat val = *(col++)*brightness*nopacity;
44885               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
44886               ptrd+=off;
44887             }
44888           } else { // Brightness>1
44889             cimg_forC(*this,c) {
44890               const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*_sc_maxval)*nopacity;
44891               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
44892               ptrd+=off;
44893             }
44894           }
44895         }
44896       }
44897       return *this;
44898     }
44899 
44900     //! Draw a 3D point.
44901     /**
44902        \param x0 X-coordinate of the point.
44903        \param y0 Y-coordinate of the point.
44904        \param z0 Z-coordinate of the point.
44905        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44906        \param opacity Drawing opacity.
44907        \note
44908        - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
44909        \par Example:
44910        \code
44911        CImg<unsigned char> img(100,100,1,3,0);
44912        const unsigned char color[] = { 255,128,64 };
44913        img.draw_point(50,50,color);
44914        \endcode
44915     **/
44916     template<typename tc>
44917     CImg<T>& draw_point(const int x0, const int y0, const int z0,
44918                         const tc *const color, const float opacity=1) {
44919       if (is_empty()) return *this;
44920       if (!color)
44921         throw CImgArgumentException(_cimg_instance
44922                                     "draw_point(): Specified color is (null).",
44923                                     cimg_instance);
44924       if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
44925         const ulongT whd = (ulongT)_width*_height*_depth;
44926         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
44927         T *ptrd = data(x0,y0,z0,0);
44928         const tc *col = color;
44929         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
44930         else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
44931       }
44932       return *this;
44933     }
44934 
44935     //! Draw a 2D point \simplification.
44936     template<typename tc>
44937     CImg<T>& draw_point(const int x0, const int y0,
44938                         const tc *const color, const float opacity=1) {
44939       return draw_point(x0,y0,0,color,opacity);
44940     }
44941 
44942     // Draw a points cloud.
44943     /**
44944        \param points Image of vertices coordinates.
44945        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44946        \param opacity Drawing opacity.
44947     **/
44948     template<typename t, typename tc>
44949     CImg<T>& draw_point(const CImg<t>& points,
44950                         const tc *const color, const float opacity=1) {
44951       if (is_empty() || !points) return *this;
44952       switch (points._height) {
44953       case 0 : case 1 :
44954         throw CImgArgumentException(_cimg_instance
44955                                     "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
44956                                     cimg_instance,
44957                                     points._width,points._height,points._depth,points._spectrum,points._data);
44958       case 2 : {
44959         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
44960       } break;
44961       default : {
44962         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
44963       }
44964       }
44965       return *this;
44966     }
44967 
44968     //! Draw a 2D line.
44969     /**
44970        \param x0 X-coordinate of the starting line point.
44971        \param y0 Y-coordinate of the starting line point.
44972        \param x1 X-coordinate of the ending line point.
44973        \param y1 Y-coordinate of the ending line point.
44974        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
44975        \param opacity Drawing opacity.
44976        \param pattern An integer whose bits describe the line pattern.
44977        \param init_hatch Tells if a reinitialization of the hash state must be done.
44978        \note
44979        - Line routine uses Bresenham's algorithm.
44980        - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
44981        \par Example:
44982        \code
44983        CImg<unsigned char> img(100,100,1,3,0);
44984        const unsigned char color[] = { 255,128,64 };
44985         img.draw_line(40,40,80,70,color);
44986        \endcode
44987     **/
44988     template<typename tc>
44989     CImg<T>& draw_line(int x0, int y0,
44990                        int x1, int y1,
44991                        const tc *const color, const float opacity=1,
44992                        const unsigned int pattern=~0U, const bool init_hatch=true) {
44993       if (is_empty() || !opacity || !pattern ||
44994           std::min(y0,y1)>=height() || std::max(y0,y1)<0 ||
44995           std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
44996 
44997       int
44998         w1 = width() - 1, h1 = height() - 1,
44999         dx01 = x1 - x0, dy01 = y1 - y0;
45000 
45001       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45002       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45003       if (pattern==~0U && y0>y1) {
45004         cimg::swap(x0,x1,y0,y1);
45005         dx01*=-1; dy01*=-1;
45006       }
45007 
45008       static unsigned int hatch = ~0U - (~0U>>1);
45009       if (init_hatch) hatch = ~0U - (~0U>>1);
45010       cimg_init_scanline(opacity);
45011       const int
45012         step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2,
45013         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45014       dy01+=dy01?0:1;
45015 
45016       for (int y = cy0; y!=cy1; y+=step) {
45017         const int
45018           yy0 = y - y0,
45019           x = x0 + (dx01*yy0 + hdy01)/dy01;
45020         if (x>=0 && x<=w1 && pattern&hatch) {
45021           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45022           cimg_forC(*this,c) {
45023             const T val = color[c];
45024             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45025           }
45026         }
45027         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45028       }
45029       return *this;
45030     }
45031 
45032     //! Draw a 2D line, with z-buffering.
45033     /**
45034        \param zbuffer Zbuffer image.
45035        \param x0 X-coordinate of the starting point.
45036        \param y0 Y-coordinate of the starting point.
45037        \param z0 Z-coordinate of the starting point
45038        \param x1 X-coordinate of the ending point.
45039        \param y1 Y-coordinate of the ending point.
45040        \param z1 Z-coordinate of the ending point.
45041        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45042        \param opacity Drawing opacity.
45043        \param pattern An integer whose bits describe the line pattern.
45044        \param init_hatch Tells if a reinitialization of the hash state must be done.
45045     **/
45046     template<typename tz,typename tc>
45047     CImg<T>& draw_line(CImg<tz>& zbuffer,
45048                        int x0, int y0, const float z0,
45049                        int x1, int y1, const float z1,
45050                        const tc *const color, const float opacity=1,
45051                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45052       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
45053       if (!color)
45054         throw CImgArgumentException(_cimg_instance
45055                                     "draw_line(): Specified color is (null).",
45056                                     cimg_instance);
45057       if (!is_sameXY(zbuffer))
45058         throw CImgArgumentException(_cimg_instance
45059                                     "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
45060                                     "different dimensions.",
45061                                     cimg_instance,
45062                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
45063 
45064       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45065 
45066       float iz0 = 1/z0, iz1 = 1/z1;
45067       int
45068         w1 = width() - 1, h1 = height() - 1,
45069         dx01 = x1 - x0, dy01 = y1 - y0;
45070       float diz01 = iz1 - iz0;
45071 
45072       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45073       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45074       if (pattern==~0U && y0>y1) {
45075         cimg::swap(x0,x1,y0,y1,iz0,iz1);
45076         dx01*=-1; dy01*=-1; diz01*=-1;
45077       }
45078 
45079       static unsigned int hatch = ~0U - (~0U>>1);
45080       if (init_hatch) hatch = ~0U - (~0U>>1);
45081       cimg_init_scanline(opacity);
45082 
45083       const int
45084         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
45085         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45086       dy01+=dy01?0:1;
45087 
45088       for (int y = cy0; y!=cy1; y+=step) {
45089         const int
45090           yy0 = y - y0,
45091           x = x0 + (dx01*yy0 + hdy01)/dy01;
45092         const float iz = iz0 + diz01*yy0/dy01;
45093         tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
45094 
45095         if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
45096           *ptrz = (tz)iz;
45097           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45098           cimg_forC(*this,c) {
45099             const T val = color[c];
45100             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45101           }
45102         }
45103         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45104       }
45105       return *this;
45106     }
45107 
45108     //! Draw a textured 2D line.
45109     /**
45110        \param x0 X-coordinate of the starting line point.
45111        \param y0 Y-coordinate of the starting line point.
45112        \param x1 X-coordinate of the ending line point.
45113        \param y1 Y-coordinate of the ending line point.
45114        \param texture Texture image defining the pixel colors.
45115        \param tx0 X-coordinate of the starting texture point.
45116        \param ty0 Y-coordinate of the starting texture point.
45117        \param tx1 X-coordinate of the ending texture point.
45118        \param ty1 Y-coordinate of the ending texture point.
45119        \param opacity Drawing opacity.
45120        \param pattern An integer whose bits describe the line pattern.
45121        \param init_hatch Tells if the hash variable must be reinitialized.
45122        \note
45123        - Line routine uses the well known Bresenham's algorithm.
45124        \par Example:
45125        \code
45126        CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
45127        const unsigned char color[] = { 255,128,64 };
45128        img.draw_line(40,40,80,70,texture,0,0,255,255);
45129        \endcode
45130     **/
45131     template<typename tc>
45132     CImg<T>& draw_line(int x0, int y0,
45133                        int x1, int y1,
45134                        const CImg<tc>& texture,
45135                        int tx0, int ty0,
45136                        int tx1, int ty1,
45137                        const float opacity=1,
45138                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45139 
45140       if (is_empty() || !opacity || !pattern) return *this;
45141       if (texture._depth>1 || texture._spectrum<_spectrum)
45142         throw CImgArgumentException(_cimg_instance
45143                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
45144                                     cimg_instance,
45145                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
45146       if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
45147 
45148       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45149 
45150       int
45151         w1 = width() - 1, h1 = height() - 1,
45152         dx01 = x1 - x0, dy01 = y1 - y0;
45153       int
45154         dtx01 = tx1 - tx0, dty01 = ty1 - ty0;
45155 
45156       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45157       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45158       if (pattern==~0U && y0>y1) {
45159         cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
45160         dx01*=-1; dy01*=-1; dtx01*=-1; dty01*=-1;
45161       }
45162 
45163       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
45164       static unsigned int hatch = ~0U - (~0U>>1);
45165       if (init_hatch) hatch = ~0U - (~0U>>1);
45166       cimg_init_scanline(opacity);
45167 
45168       const int
45169         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
45170         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy01ty = dy01*cimg::sign(dty01)/2,
45171         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45172       dy01+=dy01?0:1;
45173 
45174       for (int y = cy0; y!=cy1; y+=step) {
45175         const int
45176           yy0 = y - y0,
45177           x = x0 + (dx01*yy0 + hdy01)/dy01,
45178           tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01,
45179           ty = ty0 + (dty01*yy0 + hdy01ty)/dy01;
45180         if (x>=0 && x<=w1 && pattern&hatch) {
45181           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45182           const tc *const color = &texture._atXY(tx,ty);
45183           cimg_forC(*this,c) {
45184             const T val = color[c*twhd];
45185             ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45186           }
45187         }
45188         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45189       }
45190       return *this;
45191     }
45192 
45193     //! Draw a textured 2D line, with perspective correction.
45194     /**
45195        \param x0 X-coordinate of the starting point.
45196        \param y0 Y-coordinate of the starting point.
45197        \param z0 Z-coordinate of the starting point
45198        \param x1 X-coordinate of the ending point.
45199        \param y1 Y-coordinate of the ending point.
45200        \param z1 Z-coordinate of the ending point.
45201        \param texture Texture image defining the pixel colors.
45202        \param tx0 X-coordinate of the starting texture point.
45203        \param ty0 Y-coordinate of the starting texture point.
45204        \param tx1 X-coordinate of the ending texture point.
45205        \param ty1 Y-coordinate of the ending texture point.
45206        \param opacity Drawing opacity.
45207        \param pattern An integer whose bits describe the line pattern.
45208        \param init_hatch Tells if the hash variable must be reinitialized.
45209     **/
45210     template<typename tc>
45211     CImg<T>& draw_line(int x0, int y0, const float z0,
45212                        int x1, int y1, const float z1,
45213                        const CImg<tc>& texture,
45214                        const int tx0, const int ty0,
45215                        const int tx1, const int ty1,
45216                        const float opacity=1,
45217                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45218       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
45219       if (texture._depth>1 || texture._spectrum<_spectrum)
45220         throw CImgArgumentException(_cimg_instance
45221                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
45222                                     cimg_instance,
45223                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
45224       if (is_overlapped(texture))
45225         return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
45226 
45227       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45228 
45229       float iz0 = 1/z0, iz1 = 1/z1;
45230       int
45231         w1 = width() - 1, h1 = height() - 1,
45232         dx01 = x1 - x0, dy01 = y1 - y0;
45233       float
45234         diz01 = iz1 - iz0,
45235         txz0 = tx0*iz0, txz1 = tx1*iz1,
45236         tyz0 = ty0*iz0, tyz1 = ty1*iz1,
45237         dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
45238 
45239       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45240       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45241       if (pattern==~0U && y0>y1) {
45242         cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
45243         dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
45244       }
45245 
45246       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
45247       static unsigned int hatch = ~0U - (~0U>>1);
45248       if (init_hatch) hatch = ~0U - (~0U>>1);
45249       cimg_init_scanline(opacity);
45250 
45251       const int
45252         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
45253         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45254       dy01+=dy01?0:1;
45255 
45256       for (int y = cy0; y!=cy1; y+=step) {
45257         const int
45258           yy0 = y - y0,
45259           x = x0 + (dx01*yy0 + hdy01)/dy01;
45260         const float
45261           iz = iz0 + diz01*yy0/dy01,
45262           txz = txz0 + dtxz01*yy0/dy01,
45263           tyz = tyz0 + dtyz01*yy0/dy01;
45264         if (x>=0 && x<=w1 && pattern&hatch) {
45265           const int
45266             tx = (int)cimg::round(txz/iz),
45267             ty = (int)cimg::round(tyz/iz);
45268           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45269           const tc *const color = &texture._atXY(tx,ty);
45270           cimg_forC(*this,c) {
45271             const T val = color[c*twhd];
45272             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45273           }
45274         }
45275         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45276       }
45277       return *this;
45278     }
45279 
45280     //! Draw a textured 2D line, with perspective correction and z-buffering.
45281     /**
45282        \param zbuffer Z-buffer image.
45283        \param x0 X-coordinate of the starting point.
45284        \param y0 Y-coordinate of the starting point.
45285        \param z0 Z-coordinate of the starting point
45286        \param x1 X-coordinate of the ending point.
45287        \param y1 Y-coordinate of the ending point.
45288        \param z1 Z-coordinate of the ending point.
45289        \param texture Texture image defining the pixel colors.
45290        \param tx0 X-coordinate of the starting texture point.
45291        \param ty0 Y-coordinate of the starting texture point.
45292        \param tx1 X-coordinate of the ending texture point.
45293        \param ty1 Y-coordinate of the ending texture point.
45294        \param opacity Drawing opacity.
45295        \param pattern An integer whose bits describe the line pattern.
45296        \param init_hatch Tells if the hash variable must be reinitialized.
45297     **/
45298     template<typename tz, typename tc>
45299     CImg<T>& draw_line(CImg<tz>& zbuffer,
45300                        int x0, int y0, const float z0,
45301                        int x1, int y1, const float z1,
45302                        const CImg<tc>& texture,
45303                        const int tx0, const int ty0,
45304                        const int tx1, const int ty1,
45305                        const float opacity=1,
45306                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45307       if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
45308       if (!is_sameXY(zbuffer))
45309         throw CImgArgumentException(_cimg_instance
45310                                     "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
45311                                     "different dimensions.",
45312                                     cimg_instance,
45313                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
45314       if (texture._depth>1 || texture._spectrum<_spectrum)
45315         throw CImgArgumentException(_cimg_instance
45316                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
45317                                     cimg_instance,
45318                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
45319       if (is_overlapped(texture))
45320         return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
45321 
45322       if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
45323 
45324       float iz0 = 1/z0, iz1 = 1/z1;
45325       int
45326         w1 = width() - 1, h1 = height() - 1,
45327         dx01 = x1 - x0, dy01 = y1 - y0;
45328       float
45329         diz01 = iz1 - iz0,
45330         txz0 = tx0*iz0, txz1 = tx1*iz1,
45331         tyz0 = ty0*iz0, tyz1 = ty1*iz1,
45332         dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
45333 
45334       const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
45335       if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
45336       if (pattern==~0U && y0>y1) {
45337         cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
45338         dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
45339       }
45340 
45341       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
45342       static unsigned int hatch = ~0U - (~0U>>1);
45343       if (init_hatch) hatch = ~0U - (~0U>>1);
45344       cimg_init_scanline(opacity);
45345 
45346       const int
45347         step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
45348         cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
45349       dy01+=dy01?0:1;
45350 
45351       for (int y = cy0; y!=cy1; y+=step) {
45352         const int
45353           yy0 = y - y0,
45354           x = x0 + (dx01*yy0 + hdy01)/dy01;
45355         const float
45356           iz = iz0 + diz01*yy0/dy01,
45357           txz = txz0 + dtxz01*yy0/dy01,
45358           tyz = tyz0 + dtyz01*yy0/dy01;
45359         tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
45360 
45361         if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
45362           *ptrz = (tz)iz;
45363           const int
45364             tx = (int)cimg::round(txz/iz),
45365             ty = (int)cimg::round(tyz/iz);
45366           T *const ptrd = is_horizontal?data(y,x):data(x,y);
45367           const tc *const color = &texture._atXY(tx,ty);
45368           cimg_forC(*this,c) {
45369             const T val = color[c*twhd];
45370             ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45371           }
45372         }
45373         if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
45374       }
45375       return *this;
45376     }
45377 
45378     //! Draw a set of consecutive lines.
45379     /**
45380        \param points Coordinates of vertices, stored as a list of vectors.
45381        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45382        \param opacity Drawing opacity.
45383        \param pattern An integer whose bits describe the line pattern.
45384        \param init_hatch If set to true, init hatch motif.
45385        \note
45386        - This function uses several call to the single CImg::draw_line() procedure,
45387        depending on the vectors size in \p points.
45388     **/
45389     template<typename t, typename tc>
45390     CImg<T>& draw_line(const CImg<t>& points,
45391                        const tc *const color, const float opacity=1,
45392                        const unsigned int pattern=~0U, const bool init_hatch=true) {
45393       if (is_empty() || !points || points._width<2) return *this;
45394       bool ninit_hatch = init_hatch;
45395       switch (points._height) {
45396       case 0 : case 1 :
45397         throw CImgArgumentException(_cimg_instance
45398                                     "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).",
45399                                     cimg_instance,
45400                                     points._width,points._height,points._depth,points._spectrum,points._data);
45401 
45402       default : {
45403         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
45404         int ox = x0, oy = y0;
45405         for (unsigned int i = 1; i<points._width; ++i) {
45406           const int x = (int)points(i,0), y = (int)points(i,1);
45407           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
45408           ninit_hatch = false;
45409           ox = x; oy = y;
45410         }
45411       }
45412       }
45413       return *this;
45414     }
45415 
45416     //! Draw a 2D arrow.
45417     /**
45418        \param x0 X-coordinate of the starting arrow point (tail).
45419        \param y0 Y-coordinate of the starting arrow point (tail).
45420        \param x1 X-coordinate of the ending arrow point (head).
45421        \param y1 Y-coordinate of the ending arrow point (head).
45422        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45423        \param angle Aperture angle of the arrow head.
45424        \param length Length of the arrow head. If negative, describes a percentage of the arrow length.
45425        \param opacity Drawing opacity.
45426        \param pattern An integer whose bits describe the line pattern.
45427     **/
45428     template<typename tc>
45429     CImg<T>& draw_arrow(const int x0, const int y0,
45430                         const int x1, const int y1,
45431                         const tc *const color, const float opacity=1,
45432                         const float angle=30, const float length=-10,
45433                         const unsigned int pattern=~0U) {
45434       if (is_empty()) return *this;
45435       const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
45436         deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f,
45437         l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
45438       if (sq>0) {
45439         const float
45440             cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
45441             cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
45442         const int
45443           xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
45444           xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
45445           xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2;
45446         draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
45447       } else draw_point(x0,y0,color,opacity);
45448       return *this;
45449     }
45450 
45451     //! Draw a 2D spline.
45452     /**
45453        \param x0 X-coordinate of the starting curve point
45454        \param y0 Y-coordinate of the starting curve point
45455        \param u0 X-coordinate of the starting velocity
45456        \param v0 Y-coordinate of the starting velocity
45457        \param x1 X-coordinate of the ending curve point
45458        \param y1 Y-coordinate of the ending curve point
45459        \param u1 X-coordinate of the ending velocity
45460        \param v1 Y-coordinate of the ending velocity
45461        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45462        \param precision Curve drawing precision.
45463        \param opacity Drawing opacity.
45464        \param pattern An integer whose bits describe the line pattern.
45465        \param init_hatch If \c true, init hatch motif.
45466        \note
45467        - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points
45468        and corresponding velocity vectors.
45469        - The spline is drawn as a sequence of connected segments. The \p precision parameter sets the
45470        average number of pixels in each drawn segment.
45471        - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya),
45472          (\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
45473          and (\p xa,\p ya), (\p xb,\p yb) are two
45474        \e control points.
45475        The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from
45476        the control points as
45477        \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).
45478        \par Example:
45479        \code
45480        CImg<unsigned char> img(100,100,1,3,0);
45481        const unsigned char color[] = { 255,255,255 };
45482        img.draw_spline(30,30,0,100,90,40,0,-100,color);
45483        \endcode
45484     **/
45485     template<typename tc>
45486     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
45487                          const int x1, const int y1, const float u1, const float v1,
45488                          const tc *const color, const float opacity=1,
45489                          const float precision=0.25, const unsigned int pattern=~0U,
45490                          const bool init_hatch=true) {
45491       if (is_empty()) return *this;
45492       if (!color)
45493         throw CImgArgumentException(_cimg_instance
45494                                     "draw_spline(): Specified color is (null).",
45495                                     cimg_instance);
45496       if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
45497       bool ninit_hatch = init_hatch;
45498       const float
45499         ax = u0 + u1 + 2*(x0 - x1),
45500         bx = 3*(x1 - x0) - 2*u0 - u1,
45501         ay = v0 + v1 + 2*(y0 - y1),
45502         by = 3*(y1 - y0) - 2*v0 - v1,
45503         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
45504       int ox = x0, oy = y0;
45505       for (float t = 0; t<1; t+=_precision) {
45506         const float t2 = t*t, t3 = t2*t;
45507         const int
45508           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
45509           ny = (int)(ay*t3 + by*t2 + v0*t + y0);
45510         draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
45511         ninit_hatch = false;
45512         ox = nx; oy = ny;
45513       }
45514       return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
45515     }
45516 
45517     //! Draw a textured 2D spline.
45518     /**
45519        \param x0 X-coordinate of the starting curve point
45520        \param y0 Y-coordinate of the starting curve point
45521        \param u0 X-coordinate of the starting velocity
45522        \param v0 Y-coordinate of the starting velocity
45523        \param x1 X-coordinate of the ending curve point
45524        \param y1 Y-coordinate of the ending curve point
45525        \param u1 X-coordinate of the ending velocity
45526        \param v1 Y-coordinate of the ending velocity
45527        \param texture Texture image defining line pixel colors.
45528        \param tx0 X-coordinate of the starting texture point.
45529        \param ty0 Y-coordinate of the starting texture point.
45530        \param tx1 X-coordinate of the ending texture point.
45531        \param ty1 Y-coordinate of the ending texture point.
45532        \param precision Curve drawing precision.
45533        \param opacity Drawing opacity.
45534        \param pattern An integer whose bits describe the line pattern.
45535        \param init_hatch if \c true, reinit hatch motif.
45536     **/
45537     template<typename t>
45538     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
45539                          const int x1, const int y1, const float u1, const float v1,
45540                          const CImg<t>& texture,
45541                          const int tx0, const int ty0, const int tx1, const int ty1,
45542                          const float opacity=1,
45543                          const float precision=4, const unsigned int pattern=~0U,
45544                          const bool init_hatch=true) {
45545       if (texture._depth>1 || texture._spectrum<_spectrum)
45546         throw CImgArgumentException(_cimg_instance
45547                                     "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
45548                                     cimg_instance,
45549                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
45550       if (is_empty()) return *this;
45551       if (is_overlapped(texture))
45552         return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
45553       if (x0==x1 && y0==y1)
45554         return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
45555                                                       y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).data(),
45556                           opacity);
45557       bool ninit_hatch = init_hatch;
45558       const float
45559         ax = u0 + u1 + 2*(x0 - x1),
45560         bx = 3*(x1 - x0) - 2*u0 - u1,
45561         ay = v0 + v1 + 2*(y0 - y1),
45562         by = 3*(y1 - y0) - 2*v0 - v1,
45563         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
45564       int ox = x0, oy = y0, otx = tx0, oty = ty0;
45565       for (float t1 = 0; t1<1; t1+=_precision) {
45566         const float t2 = t1*t1, t3 = t2*t1;
45567         const int
45568           nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
45569           ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
45570           ntx = tx0 + (int)((tx1 - tx0)*t1),
45571           nty = ty0 + (int)((ty1 - ty0)*t1);
45572         draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
45573         ninit_hatch = false;
45574         ox = nx; oy = ny; otx = ntx; oty = nty;
45575       }
45576       return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
45577     }
45578 
45579     //! Draw a set of consecutive splines.
45580     /**
45581        \param points Vertices data.
45582        \param tangents Tangents data.
45583        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45584        \param opacity Drawing opacity.
45585        \param is_closed_set Tells if the drawn spline set is closed.
45586        \param precision Precision of the drawing.
45587        \param pattern An integer whose bits describe the line pattern.
45588        \param init_hatch If \c true, init hatch motif.
45589     **/
45590     template<typename tp, typename tt, typename tc>
45591     CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
45592                          const tc *const color, const float opacity=1,
45593                          const bool is_closed_set=false, const float precision=4,
45594                          const unsigned int pattern=~0U, const bool init_hatch=true) {
45595       if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
45596       bool ninit_hatch = init_hatch;
45597       switch (points._height) {
45598       case 0 : case 1 :
45599         throw CImgArgumentException(_cimg_instance
45600                                     "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
45601                                     cimg_instance,
45602                                     points._width,points._height,points._depth,points._spectrum,points._data);
45603 
45604       default : {
45605         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
45606         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
45607         int ox = x0, oy = y0;
45608         float ou = u0, ov = v0;
45609         for (unsigned int i = 1; i<points._width; ++i) {
45610           const int x = (int)points(i,0), y = (int)points(i,1);
45611           const float u = (float)tangents(i,0), v = (float)tangents(i,1);
45612           draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
45613           ninit_hatch = false;
45614           ox = x; oy = y; ou = u; ov = v;
45615         }
45616         if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
45617       }
45618       }
45619       return *this;
45620     }
45621 
45622     //! Draw a set of consecutive splines \overloading.
45623     /**
45624        Similar to previous function, with the point tangents automatically estimated from the given points set.
45625     **/
45626     template<typename tp, typename tc>
45627     CImg<T>& draw_spline(const CImg<tp>& points,
45628                          const tc *const color, const float opacity=1,
45629                          const bool is_closed_set=false, const float precision=4,
45630                          const unsigned int pattern=~0U, const bool init_hatch=true) {
45631       if (is_empty() || !points || points._width<2) return *this;
45632       CImg<Tfloat> tangents;
45633       switch (points._height) {
45634       case 0 : case 1 :
45635         throw CImgArgumentException(_cimg_instance
45636                                     "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
45637                                     cimg_instance,
45638                                     points._width,points._height,points._depth,points._spectrum,points._data);
45639       case 2 : {
45640         tangents.assign(points._width,points._height);
45641         cimg_forX(points,p) {
45642           const unsigned int
45643             p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
45644             p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
45645           const float
45646             x = (float)points(p,0),
45647             y = (float)points(p,1),
45648             x0 = (float)points(p0,0),
45649             y0 = (float)points(p0,1),
45650             x1 = (float)points(p1,0),
45651             y1 = (float)points(p1,1),
45652             u0 = x - x0,
45653             v0 = y - y0,
45654             n0 = 1e-8f + cimg::hypot(u0,v0),
45655             u1 = x1 - x,
45656             v1 = y1 - y,
45657             n1 = 1e-8f + cimg::hypot(u1,v1),
45658             u = u0/n0 + u1/n1,
45659             v = v0/n0 + v1/n1,
45660             n = 1e-8f + cimg::hypot(u,v),
45661             fact = 0.5f*(n0 + n1);
45662           tangents(p,0) = (Tfloat)(fact*u/n);
45663           tangents(p,1) = (Tfloat)(fact*v/n);
45664         }
45665       } break;
45666       default : {
45667         tangents.assign(points._width,points._height);
45668         cimg_forX(points,p) {
45669           const unsigned int
45670             p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
45671             p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
45672           const float
45673             x = (float)points(p,0),
45674             y = (float)points(p,1),
45675             z = (float)points(p,2),
45676             x0 = (float)points(p0,0),
45677             y0 = (float)points(p0,1),
45678             z0 = (float)points(p0,2),
45679             x1 = (float)points(p1,0),
45680             y1 = (float)points(p1,1),
45681             z1 = (float)points(p1,2),
45682             u0 = x - x0,
45683             v0 = y - y0,
45684             w0 = z - z0,
45685             n0 = 1e-8f + cimg::hypot(u0,v0,w0),
45686             u1 = x1 - x,
45687             v1 = y1 - y,
45688             w1 = z1 - z,
45689             n1 = 1e-8f + cimg::hypot(u1,v1,w1),
45690             u = u0/n0 + u1/n1,
45691             v = v0/n0 + v1/n1,
45692             w = w0/n0 + w1/n1,
45693             n = 1e-8f + cimg::hypot(u,v,w),
45694             fact = 0.5f*(n0 + n1);
45695           tangents(p,0) = (Tfloat)(fact*u/n);
45696           tangents(p,1) = (Tfloat)(fact*v/n);
45697           tangents(p,2) = (Tfloat)(fact*w/n);
45698         }
45699       }
45700       }
45701       return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
45702     }
45703 
45704     // [internal] Draw a filled triangle.
45705     template<typename tc>
45706     CImg<T>& _draw_triangle(int x0, int y0,
45707                             int x1, int y1,
45708                             int x2, int y2,
45709                             const tc *const color, const float opacity,
45710                             const float brightness) {
45711       if (y0>y1) cimg::swap(x0,x1,y0,y1);
45712       if (y0>y2) cimg::swap(x0,x2,y0,y2);
45713       if (y1>y2) cimg::swap(x1,x2,y1,y2);
45714       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
45715 
45716       const int
45717         h1 = height() - 1,
45718         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
45719         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
45720         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
45721         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
45722 
45723       const float cbs = cimg::cut(brightness,0,2);
45724       cimg_init_scanline(opacity);
45725 
45726       for (int y = cy0; y<=cy2; ++y) {
45727         const int yy0 = y - y0, yy1 = y - y1;
45728         int
45729           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
45730           xM = x0 + (dx02*yy0 + hdy02)/dy02;
45731         if (xm>xM) cimg::swap(xm,xM);
45732         cimg_draw_scanline(xm,xM,y,color,opacity,cbs);
45733       }
45734       return *this;
45735     }
45736 
45737     //! Draw a filled 2D triangle.
45738     /**
45739        \param x0 X-coordinate of the first vertex.
45740        \param y0 Y-coordinate of the first vertex.
45741        \param x1 X-coordinate of the second vertex.
45742        \param y1 Y-coordinate of the second vertex.
45743        \param x2 X-coordinate of the third vertex.
45744        \param y2 Y-coordinate of the third vertex.
45745        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45746        \param opacity Drawing opacity.
45747      **/
45748     template<typename tc>
45749     CImg<T>& draw_triangle(const int x0, const int y0,
45750                            const int x1, const int y1,
45751                            const int x2, const int y2,
45752                            const tc *const color, const float opacity=1) {
45753       if (is_empty()) return *this;
45754       if (!color)
45755         throw CImgArgumentException(_cimg_instance
45756                                     "draw_triangle(): Specified color is (null).",
45757                                     cimg_instance);
45758       _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
45759       return *this;
45760     }
45761 
45762     //! Draw a outlined 2D triangle.
45763     /**
45764        \param x0 X-coordinate of the first vertex.
45765        \param y0 Y-coordinate of the first vertex.
45766        \param x1 X-coordinate of the second vertex.
45767        \param y1 Y-coordinate of the second vertex.
45768        \param x2 X-coordinate of the third vertex.
45769        \param y2 Y-coordinate of the third vertex.
45770        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45771        \param opacity Drawing opacity.
45772        \param pattern An integer whose bits describe the outline pattern.
45773      **/
45774     template<typename tc>
45775     CImg<T>& draw_triangle(const int x0, const int y0,
45776                            const int x1, const int y1,
45777                            const int x2, const int y2,
45778                            const tc *const color, const float opacity,
45779                            const unsigned int pattern) {
45780       if (is_empty()) return *this;
45781       if (!color)
45782         throw CImgArgumentException(_cimg_instance
45783                                     "draw_triangle(): Specified color is (null).",
45784                                     cimg_instance);
45785       draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
45786         draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
45787         draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
45788       return *this;
45789     }
45790 
45791     //! Draw a filled 2D triangle, with z-buffering.
45792     /**
45793        \param zbuffer Z-buffer image.
45794        \param x0 X-coordinate of the first vertex.
45795        \param y0 Y-coordinate of the first vertex.
45796        \param z0 Z-coordinate of the first vertex.
45797        \param x1 X-coordinate of the second vertex.
45798        \param y1 Y-coordinate of the second vertex.
45799        \param z1 Z-coordinate of the second vertex.
45800        \param x2 X-coordinate of the third vertex.
45801        \param y2 Y-coordinate of the third vertex.
45802        \param z2 Z-coordinate of the third vertex.
45803        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
45804        \param opacity Drawing opacity.
45805        \param brightness Brightness factor.
45806     **/
45807     template<typename tz, typename tc>
45808     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
45809                            int x0, int y0, const float z0,
45810                            int x1, int y1, const float z1,
45811                            int x2, int y2, const float z2,
45812                            const tc *const color, const float opacity=1,
45813                            const float brightness=1) {
45814       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
45815       if (!color)
45816         throw CImgArgumentException(_cimg_instance
45817                                     "draw_triangle(): Specified color is (null).",
45818                                     cimg_instance);
45819       if (!is_sameXY(zbuffer))
45820         throw CImgArgumentException(_cimg_instance
45821                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
45822                                     "different dimensions.",
45823                                     cimg_instance,
45824                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
45825 
45826       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
45827       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1);
45828       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2);
45829       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2);
45830       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
45831 
45832       const int
45833         w1 = width() - 1, h1 = height() - 1,
45834         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
45835         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
45836         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
45837         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
45838       const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
45839 
45840       const float cbs = cimg::cut(brightness,0,2);
45841       cimg_init_scanline(opacity);
45842 
45843       for (int y = cy0; y<=cy2; ++y) {
45844         const int yy0 = y - y0, yy1 = y - y1;
45845         int
45846           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
45847           xM = x0 + (dx02*yy0 + hdy02)/dy02;
45848         float
45849           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
45850           izM = iz0 + diz02*yy0/dy02;
45851         if (xm>xM) cimg::swap(xm,xM,izm,izM);
45852         if (xM>=0 && xm<=w1) {
45853           const int
45854             cxm = cimg::cut(xm,0,w1),
45855             cxM = cimg::cut(xM,0,w1);
45856           T *ptrd = data(cxm,y);
45857           tz *ptrz = zbuffer.data(cxm,y);
45858           const int dxmM = std::max(1,xM - xm);
45859           const float dizmM = izM - izm;
45860 
45861           for (int x = cxm; x<=cxM; ++x) {
45862             const int xxm = x - xm;
45863             const float iz = izm + dizmM*xxm/dxmM;
45864             if (iz>=*ptrz) {
45865               *ptrz = (tz)iz;
45866               cimg_forC(*this,c) {
45867                 const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
45868                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45869               }
45870             }
45871             ++ptrd; ++ptrz;
45872           }
45873         }
45874       }
45875       return *this;
45876     }
45877 
45878     //! Draw a Gouraud-shaded 2D triangle.
45879     /**
45880        \param x0 X-coordinate of the first vertex in the image instance.
45881        \param y0 Y-coordinate of the first vertex in the image instance.
45882        \param x1 X-coordinate of the second vertex in the image instance.
45883        \param y1 Y-coordinate of the second vertex in the image instance.
45884        \param x2 X-coordinate of the third vertex in the image instance.
45885        \param y2 Y-coordinate of the third vertex in the image instance.
45886        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45887        \param bs0 Brightness factor of the first vertex (in [0,2]).
45888        \param bs1 brightness factor of the second vertex (in [0,2]).
45889        \param bs2 brightness factor of the third vertex (in [0,2]).
45890        \param opacity Drawing opacity.
45891     **/
45892     template<typename tc>
45893     CImg<T>& draw_triangle(int x0, int y0,
45894                            int x1, int y1,
45895                            int x2, int y2,
45896                            const tc *const color,
45897                            float bs0,
45898                            float bs1,
45899                            float bs2,
45900                            const float opacity=1) {
45901       if (is_empty()) return *this;
45902       if (!color)
45903         throw CImgArgumentException(_cimg_instance
45904                                     "draw_triangle(): Specified color is (null).",
45905                                     cimg_instance);
45906 
45907       if (y0>y1) cimg::swap(x0,x1,y0,y1,bs0,bs1);
45908       if (y0>y2) cimg::swap(x0,x2,y0,y2,bs0,bs2);
45909       if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2);
45910       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
45911 
45912       const int
45913         w1 = width() - 1, h1 = height() - 1,
45914         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
45915         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
45916         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
45917         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
45918       const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
45919 
45920       cimg_init_scanline(opacity);
45921 
45922       for (int y = cy0; y<=cy2; ++y) {
45923         const int yy0 = y - y0, yy1 = y - y1;
45924         int
45925           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
45926           xM = x0 + (dx02*yy0 + hdy02)/dy02;
45927         float
45928           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
45929           bsM = bs0 + dbs02*yy0/dy02;
45930         if (xm>xM) cimg::swap(xm,xM,bsm,bsM);
45931         if (xM>=0 && xm<=w1) {
45932           const int
45933             cxm = cimg::cut(xm,0,w1),
45934             cxM = cimg::cut(xM,0,w1);
45935           T *ptrd = data(cxm,y);
45936           const int dxmM = std::max(1,xM - xm);
45937           const float dbsmM = bsM - bsm;
45938 
45939           for (int x = cxm; x<=cxM; ++x) {
45940             const int xxm = x - xm;
45941             const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
45942             cimg_forC(*this,c) {
45943               const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
45944               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
45945             }
45946             ++ptrd;
45947           }
45948         }
45949       }
45950       return *this;
45951     }
45952 
45953     //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading.
45954     template<typename tz, typename tc>
45955     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
45956                            int x0, int y0, const float z0,
45957                            int x1, int y1, const float z1,
45958                            int x2, int y2, const float z2,
45959                            const tc *const color,
45960                            float bs0,
45961                            float bs1,
45962                            float bs2,
45963                            float opacity=1) {
45964       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
45965       if (!color)
45966         throw CImgArgumentException(_cimg_instance
45967                                     "draw_triangle(): Specified color is (null).",
45968                                     cimg_instance);
45969       if (!is_sameXY(zbuffer))
45970         throw CImgArgumentException(_cimg_instance
45971                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
45972                                     "different dimensions.",
45973                                     cimg_instance,
45974                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
45975 
45976       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
45977       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,bs0,bs1);
45978       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,bs0,bs2);
45979       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,bs1,bs2);
45980       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
45981 
45982       const int
45983         w1 = width() - 1, h1 = height() - 1,
45984         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
45985         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
45986         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
45987         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
45988       const float
45989         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
45990         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
45991 
45992       cimg_init_scanline(opacity);
45993 
45994       for (int y = cy0; y<=cy2; ++y) {
45995         const int yy0 = y - y0, yy1 = y - y1;
45996         int
45997           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
45998           xM = x0 + (dx02*yy0 + hdy02)/dy02;
45999         float
46000           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46001           izM = iz0 + diz02*yy0/dy02,
46002           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
46003           bsM = bs0 + dbs02*yy0/dy02;
46004         if (xm>xM) cimg::swap(xm,xM,izm,izM,bsm,bsM);
46005         if (xM>=0 && xm<=w1) {
46006           const int
46007             cxm = cimg::cut(xm,0,w1),
46008             cxM = cimg::cut(xM,0,w1);
46009           T *ptrd = data(cxm,y);
46010           tz *ptrz = zbuffer.data(cxm,y);
46011           const int dxmM = std::max(1,xM - xm);
46012           const float dizmM = izM - izm, dbsmM = bsM - bsm;
46013 
46014           for (int x = cxm; x<=cxM; ++x) {
46015             const int xxm = x - xm;
46016             const float iz = izm + dizmM*xxm/dxmM;
46017             if (iz>=*ptrz) {
46018               *ptrz = (tz)iz;
46019               const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
46020               cimg_forC(*this,c) {
46021                 const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
46022                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46023               }
46024             }
46025             ++ptrd; ++ptrz;
46026           }
46027         }
46028       }
46029       return *this;
46030     }
46031 
46032     //! Draw a color-interpolated 2D triangle.
46033     /**
46034        \param x0 X-coordinate of the first vertex in the image instance.
46035        \param y0 Y-coordinate of the first vertex in the image instance.
46036        \param x1 X-coordinate of the second vertex in the image instance.
46037        \param y1 Y-coordinate of the second vertex in the image instance.
46038        \param x2 X-coordinate of the third vertex in the image instance.
46039        \param y2 Y-coordinate of the third vertex in the image instance.
46040        \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex.
46041        \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the second vertex.
46042        \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex.
46043        \param opacity Drawing opacity.
46044      **/
46045     template<typename tc1, typename tc2, typename tc3>
46046     CImg<T>& draw_triangle(const int x0, const int y0,
46047                            const int x1, const int y1,
46048                            const int x2, const int y2,
46049                            const tc1 *const color1,
46050                            const tc2 *const color2,
46051                            const tc3 *const color3,
46052                            const float opacity=1) {
46053       const unsigned char one = 1;
46054       cimg_forC(*this,c)
46055         get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
46056       return *this;
46057     }
46058 
46059     //! Draw a textured 2D triangle.
46060     /**
46061        \param x0 X-coordinate of the first vertex in the image instance.
46062        \param y0 Y-coordinate of the first vertex in the image instance.
46063        \param x1 X-coordinate of the second vertex in the image instance.
46064        \param y1 Y-coordinate of the second vertex in the image instance.
46065        \param x2 X-coordinate of the third vertex in the image instance.
46066        \param y2 Y-coordinate of the third vertex in the image instance.
46067        \param texture Texture image used to fill the triangle.
46068        \param tx0 X-coordinate of the first vertex in the texture image.
46069        \param ty0 Y-coordinate of the first vertex in the texture image.
46070        \param tx1 X-coordinate of the second vertex in the texture image.
46071        \param ty1 Y-coordinate of the second vertex in the texture image.
46072        \param tx2 X-coordinate of the third vertex in the texture image.
46073        \param ty2 Y-coordinate of the third vertex in the texture image.
46074        \param opacity Drawing opacity.
46075        \param brightness Brightness factor of the drawing (in [0,2]).
46076     **/
46077     template<typename tc>
46078     CImg<T>& draw_triangle(int x0, int y0,
46079                            int x1, int y1,
46080                            int x2, int y2,
46081                            const CImg<tc>& texture,
46082                            int tx0, int ty0,
46083                            int tx1, int ty1,
46084                            int tx2, int ty2,
46085                            const float opacity=1,
46086                            const float brightness=1) {
46087       if (is_empty()) return *this;
46088       if (texture._depth>1 || texture._spectrum<_spectrum)
46089         throw CImgArgumentException(_cimg_instance
46090                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46091                                     cimg_instance,
46092                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46093       if (is_overlapped(texture))
46094         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
46095 
46096       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
46097       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2);
46098       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,ty1,tx2,ty2);
46099       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46100 
46101       const int
46102         w1 = width() - 1, h1 = height() - 1,
46103         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46104         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46105         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46106         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
46107         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
46108         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
46109         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
46110         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
46111       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46112       const float cbs = cimg::cut(brightness,0,2);
46113       cimg_init_scanline(opacity);
46114 
46115       for (int y = cy0; y<=cy2; ++y) {
46116         const int yy0 = y - y0, yy1 = y - y1;
46117         int
46118           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46119           xM = x0 + (dx02*yy0 + hdy02)/dy02,
46120           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
46121           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
46122           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
46123           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
46124         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM);
46125         if (xM>=0 && xm<=w1) {
46126           const int
46127             cxm = cimg::cut(xm,0,w1),
46128             cxM = cimg::cut(xM,0,w1);
46129           T *ptrd = data(cxm,y);
46130           const int
46131             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
46132             dtxmM = txM - txm, dtymM = tyM - tym;
46133 
46134           for (int x = cxm; x<=cxM; ++x) {
46135             const int
46136               xxm = x - xm,
46137               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
46138               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
46139             const tc *const color = &texture._atXY(tx,ty);
46140             cimg_forC(*this,c) {
46141               const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
46142               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46143             }
46144             ++ptrd;
46145           }
46146         }
46147       }
46148       return *this;
46149     }
46150 
46151     //! Draw a 2D textured triangle, with perspective correction.
46152     template<typename tc>
46153     CImg<T>& draw_triangle(int x0, int y0, const float z0,
46154                            int x1, int y1, const float z1,
46155                            int x2, int y2, const float z2,
46156                            const CImg<tc>& texture,
46157                            int tx0, int ty0,
46158                            int tx1, int ty1,
46159                            int tx2, int ty2,
46160                            const float opacity=1,
46161                            const float brightness=1) {
46162       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46163       if (texture._depth>1 || texture._spectrum<_spectrum)
46164         throw CImgArgumentException(_cimg_instance
46165                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46166                                     cimg_instance,
46167                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46168       if (is_overlapped(texture))
46169         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
46170 
46171       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46172       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
46173       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
46174       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
46175       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46176 
46177       const int
46178         w1 = width() - 1, h1 = height() - 1,
46179         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46180         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46181         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46182         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46183       const float
46184         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
46185         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
46186         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
46187         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
46188         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
46189 
46190       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46191       const float cbs = cimg::cut(brightness,0,2);
46192       cimg_init_scanline(opacity);
46193 
46194       for (int y = cy0; y<=cy2; ++y) {
46195         const int yy0 = y - y0, yy1 = y - y1;
46196         int
46197           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46198           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46199         float
46200           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46201           izM = iz0 + diz02*yy0/dy02,
46202           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
46203           txzM = txz0 + dtxz02*yy0/dy02,
46204           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
46205           tyzM = tyz0 + dtyz02*yy0/dy02;
46206         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
46207         if (xM>=0 && xm<=w1) {
46208           const int
46209             cxm = cimg::cut(xm,0,w1),
46210             cxM = cimg::cut(xM,0,w1);
46211           T *ptrd = data(cxm,y);
46212           const int dxmM = std::max(1,xM - xm);
46213           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
46214 
46215           for (int x = cxm; x<=cxM; ++x) {
46216             const int xxm = x - xm;
46217             const float
46218               iz = izm + dizmM*xxm/dxmM,
46219               txz = txzm + dtxzmM*xxm/dxmM,
46220               tyz = tyzm + dtyzmM*xxm/dxmM;
46221             const int
46222               tx = (int)cimg::round(txz/iz),
46223               ty = (int)cimg::round(tyz/iz);
46224             const tc *const color = &texture._atXY(tx,ty);
46225             cimg_forC(*this,c) {
46226               const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
46227               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46228             }
46229             ++ptrd;
46230           }
46231         }
46232       }
46233       return *this;
46234     }
46235 
46236     //! Draw a textured 2D triangle, with perspective correction and z-buffering.
46237     template<typename tz, typename tc>
46238     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
46239                            int x0, int y0, const float z0,
46240                            int x1, int y1, const float z1,
46241                            int x2, int y2, const float z2,
46242                            const CImg<tc>& texture,
46243                            int tx0, int ty0,
46244                            int tx1, int ty1,
46245                            int tx2, int ty2,
46246                            const float opacity=1,
46247                            const float brightness=1) {
46248       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46249       if (!is_sameXY(zbuffer))
46250         throw CImgArgumentException(_cimg_instance
46251                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46252                                     "different dimensions.",
46253                                     cimg_instance,
46254                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46255 
46256       if (texture._depth>1 || texture._spectrum<_spectrum)
46257         throw CImgArgumentException(_cimg_instance
46258                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46259                                     cimg_instance,
46260                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46261       if (is_overlapped(texture))
46262         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
46263 
46264       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46265       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
46266       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
46267       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
46268       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46269 
46270       const int
46271         w1 = width() - 1, h1 = height() - 1,
46272         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46273         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46274         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46275         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46276       const float
46277         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
46278         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
46279         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
46280         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
46281         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
46282 
46283       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46284       const float cbs = cimg::cut(brightness,0,2);
46285       cimg_init_scanline(opacity);
46286 
46287       for (int y = cy0; y<=cy2; ++y) {
46288         const int yy0 = y - y0, yy1 = y - y1;
46289         int
46290           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46291           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46292         float
46293           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46294           izM = iz0 + diz02*yy0/dy02,
46295           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
46296           txzM = txz0 + dtxz02*yy0/dy02,
46297           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
46298           tyzM = tyz0 + dtyz02*yy0/dy02;
46299         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
46300         if (xM>=0 && xm<=w1) {
46301           const int
46302             cxm = cimg::cut(xm,0,w1),
46303             cxM = cimg::cut(xM,0,w1);
46304           T *ptrd = data(cxm,y);
46305           tz *ptrz = zbuffer.data(cxm,y);
46306           const int dxmM = std::max(1,xM - xm);
46307           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
46308 
46309           for (int x = cxm; x<=cxM; ++x) {
46310             const int xxm = x - xm;
46311             const float iz = izm + dizmM*xxm/dxmM;
46312             if (iz>=*ptrz) {
46313               *ptrz = (tz)iz;
46314               const float
46315                 txz = txzm + dtxzmM*xxm/dxmM,
46316                 tyz = tyzm + dtyzmM*xxm/dxmM;
46317               const int
46318                 tx = (int)cimg::round(txz/iz),
46319                 ty = (int)cimg::round(tyz/iz);
46320               const tc *const color = &texture._atXY(tx,ty);
46321               cimg_forC(*this,c) {
46322                 const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
46323                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46324               }
46325             }
46326             ++ptrd; ++ptrz;
46327           }
46328         }
46329       }
46330       return *this;
46331     }
46332 
46333     //! Draw a Phong-shaded 2D triangle.
46334     /**
46335        \param x0 X-coordinate of the first vertex in the image instance.
46336        \param y0 Y-coordinate of the first vertex in the image instance.
46337        \param x1 X-coordinate of the second vertex in the image instance.
46338        \param y1 Y-coordinate of the second vertex in the image instance.
46339        \param x2 X-coordinate of the third vertex in the image instance.
46340        \param y2 Y-coordinate of the third vertex in the image instance.
46341        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
46342        \param light Light image.
46343        \param lx0 X-coordinate of the first vertex in the light image.
46344        \param ly0 Y-coordinate of the first vertex in the light image.
46345        \param lx1 X-coordinate of the second vertex in the light image.
46346        \param ly1 Y-coordinate of the second vertex in the light image.
46347        \param lx2 X-coordinate of the third vertex in the light image.
46348        \param ly2 Y-coordinate of the third vertex in the light image.
46349        \param opacity Drawing opacity.
46350     **/
46351     template<typename tc, typename tl>
46352     CImg<T>& draw_triangle(int x0, int y0,
46353                            int x1, int y1,
46354                            int x2, int y2,
46355                            const tc *const color,
46356                            const CImg<tl>& light,
46357                            int lx0, int ly0,
46358                            int lx1, int ly1,
46359                            int lx2, int ly2,
46360                            const float opacity=1) {
46361       if (is_empty()) return *this;
46362       if (!color)
46363         throw CImgArgumentException(_cimg_instance
46364                                     "draw_triangle(): Specified color is (null).",
46365                                     cimg_instance);
46366       if (light._depth>1 || light._spectrum<_spectrum)
46367         throw CImgArgumentException(_cimg_instance
46368                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
46369                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
46370 
46371       if (y0>y1) cimg::swap(x0,x1,y0,y1,lx0,lx1,ly0,ly1);
46372       if (y0>y2) cimg::swap(x0,x2,y0,y2,lx0,lx2,ly0,ly2);
46373       if (y1>y2) cimg::swap(x1,x2,y1,y2,lx1,lx2,ly1,ly2);
46374       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46375 
46376       const int
46377         w1 = width() - 1, h1 = height() - 1,
46378         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46379         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46380         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46381         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
46382         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
46383         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
46384         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
46385         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
46386 
46387       const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
46388       cimg_init_scanline(opacity);
46389 
46390       for (int y = cy0; y<=cy2; ++y) {
46391         const int yy0 = y - y0, yy1 = y - y1;
46392         int
46393           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46394           xM = x0 + (dx02*yy0 + hdy02)/dy02,
46395           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
46396           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
46397           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
46398           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
46399         if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM);
46400         if (xM>=0 && xm<=w1) {
46401           const int
46402             cxm = cimg::cut(xm,0,w1),
46403             cxM = cimg::cut(xM,0,w1);
46404           T *ptrd = data(cxm,y);
46405           const int
46406             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
46407             dlxmM = lxM - lxm, dlymM = lyM - lym;
46408 
46409           for (int x = cxm; x<=cxM; ++x) {
46410             const int
46411               xxm = x - xm,
46412               lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
46413               ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
46414             const tl *const lig = &light._atXY(lx,ly);
46415             cimg_forC(*this,c) {
46416               const tc col = color[c];
46417               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
46418               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
46419               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46420             }
46421             ++ptrd;
46422           }
46423         }
46424       }
46425       return *this;
46426     }
46427 
46428     //! Draw a Phong-shaded 2D triangle, with z-buffering.
46429     template<typename tz, typename tc, typename tl>
46430     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
46431                            int x0, int y0, const float z0,
46432                            int x1, int y1, const float z1,
46433                            int x2, int y2, const float z2,
46434                            const tc *const color,
46435                            const CImg<tl>& light,
46436                            int lx0, int ly0,
46437                            int lx1, int ly1,
46438                            int lx2, int ly2,
46439                            const float opacity=1) {
46440       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46441       if (!color)
46442         throw CImgArgumentException(_cimg_instance
46443                                     "draw_triangle(): Specified color is (null).",
46444                                     cimg_instance);
46445       if (light._depth>1 || light._spectrum<_spectrum)
46446         throw CImgArgumentException(_cimg_instance
46447                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
46448                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
46449       if (!is_sameXY(zbuffer))
46450         throw CImgArgumentException(_cimg_instance
46451                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46452                                     "different dimensions.",
46453                                     cimg_instance,
46454                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46455       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
46456                                                      +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46457 
46458       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46459       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1);
46460       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2);
46461       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2);
46462       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46463 
46464       const int
46465         w1 = width() - 1, h1 = height() - 1,
46466         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46467         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46468         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46469         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
46470         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
46471         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
46472         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
46473         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
46474       const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
46475 
46476       const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
46477       cimg_init_scanline(opacity);
46478 
46479       for (int y = cy0; y<=cy2; ++y) {
46480         const int yy0 = y - y0, yy1 = y - y1;
46481         int
46482           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46483           xM = x0 + (dx02*yy0 + hdy02)/dy02,
46484           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
46485           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
46486           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
46487           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
46488         float
46489           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46490           izM = iz0 + diz02*yy0/dy02;
46491 
46492         if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM);
46493         if (xM>=0 && xm<=w1) {
46494           const int
46495             cxm = cimg::cut(xm,0,w1),
46496             cxM = cimg::cut(xM,0,w1);
46497           T *ptrd = data(cxm,y);
46498           tz *ptrz = zbuffer.data(cxm,y);
46499           const int
46500             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
46501             dlxmM = lxM - lxm, dlymM = lyM - lym;
46502           const float dizmM = izM - izm;
46503 
46504           for (int x = cxm; x<=cxM; ++x) {
46505             const int xxm = x - xm;
46506             const float iz = izm + dizmM*xxm/dxmM;
46507             if (iz>=*ptrz) {
46508               *ptrz = (tz)iz;
46509               const int
46510                 lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
46511                 ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
46512               const tl *const lig = &light._atXY(lx,ly);
46513               cimg_forC(*this,c) {
46514                 const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
46515                 const tc col = color[c];
46516                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
46517                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46518               }
46519             }
46520             ++ptrd; ++ptrz;
46521           }
46522         }
46523       }
46524       return *this;
46525     }
46526 
46527     //! Draw a textured Gouraud-shaded 2D triangle.
46528     /**
46529        \param x0 X-coordinate of the first vertex in the image instance.
46530        \param y0 Y-coordinate of the first vertex in the image instance.
46531        \param x1 X-coordinate of the second vertex in the image instance.
46532        \param y1 Y-coordinate of the second vertex in the image instance.
46533        \param x2 X-coordinate of the third vertex in the image instance.
46534        \param y2 Y-coordinate of the third vertex in the image instance.
46535        \param texture Texture image used to fill the triangle.
46536        \param tx0 X-coordinate of the first vertex in the texture image.
46537        \param ty0 Y-coordinate of the first vertex in the texture image.
46538        \param tx1 X-coordinate of the second vertex in the texture image.
46539        \param ty1 Y-coordinate of the second vertex in the texture image.
46540        \param tx2 X-coordinate of the third vertex in the texture image.
46541        \param ty2 Y-coordinate of the third vertex in the texture image.
46542        \param bs0 Brightness factor of the first vertex.
46543        \param bs1 Brightness factor of the second vertex.
46544        \param bs2 Brightness factor of the third vertex.
46545        \param opacity Drawing opacity.
46546     **/
46547     template<typename tc>
46548     CImg<T>& draw_triangle(int x0, int y0,
46549                            int x1, int y1,
46550                            int x2, int y2,
46551                            const CImg<tc>& texture,
46552                            int tx0, int ty0,
46553                            int tx1, int ty1,
46554                            int tx2, int ty2,
46555                            float bs0,
46556                            float bs1,
46557                            float bs2,
46558                            const float opacity=1) {
46559       if (is_empty()) return *this;
46560       if (texture._depth>1 || texture._spectrum<_spectrum)
46561         throw CImgArgumentException(_cimg_instance
46562                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46563                                     cimg_instance,
46564                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46565       if (is_overlapped(texture))
46566         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
46567                              bs0,bs1,bs2,opacity);
46568 
46569       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,bs0,bs1);
46570       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,bs0,bs2);
46571       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,bs1,bs2);
46572       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46573 
46574       const int
46575         w1 = width() - 1, h1 = height() - 1,
46576         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46577         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46578         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46579         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
46580         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
46581         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
46582         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
46583         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
46584       const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
46585 
46586       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46587       cimg_init_scanline(opacity);
46588 
46589       for (int y = cy0; y<=cy2; ++y) {
46590         const int yy0 = y - y0, yy1 = y - y1;
46591         int
46592           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46593           xM = x0 + (dx02*yy0 + hdy02)/dy02,
46594           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
46595           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
46596           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
46597           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
46598         float
46599           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
46600           bsM = bs0 + dbs02*yy0/dy02;
46601         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,bsm,bsM);
46602         if (xM>=0 && xm<=w1) {
46603           const int
46604             cxm = cimg::cut(xm,0,w1),
46605             cxM = cimg::cut(xM,0,w1);
46606           T *ptrd = data(cxm,y);
46607           const int
46608             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
46609             dtxmM = txM - txm, dtymM = tyM - tym;
46610           const float dbsmM = bsM - bsm;
46611 
46612           for (int x = cxm; x<=cxM; ++x) {
46613             const int
46614               xxm = x - xm,
46615               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
46616               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
46617             const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
46618             const tc *const color = &texture._atXY(tx,ty);
46619             cimg_forC(*this,c) {
46620               const tc col = color[c*twhd];
46621               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
46622               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46623             }
46624             ++ptrd;
46625           }
46626         }
46627       }
46628       return *this;
46629     }
46630 
46631     //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading.
46632     template<typename tc>
46633     CImg<T>& draw_triangle(int x0, int y0, const float z0,
46634                            int x1, int y1, const float z1,
46635                            int x2, int y2, const float z2,
46636                            const CImg<tc>& texture,
46637                            int tx0, int ty0,
46638                            int tx1, int ty1,
46639                            int tx2, int ty2,
46640                            float bs0,
46641                            float bs1,
46642                            float bs2,
46643                            const float opacity=1) {
46644       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46645       if (texture._depth>1 || texture._spectrum<_spectrum)
46646         throw CImgArgumentException(_cimg_instance
46647                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46648                                     cimg_instance,
46649                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46650       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
46651                                                        bs0,bs1,bs2,opacity);
46652 
46653       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46654       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
46655       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
46656       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
46657       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46658 
46659       const int
46660         w1 = width() - 1, h1 = height() - 1,
46661         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46662         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46663         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46664         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46665       const float
46666         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
46667         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
46668         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
46669         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
46670         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
46671         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
46672 
46673       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46674       cimg_init_scanline(opacity);
46675 
46676       for (int y = cy0; y<=cy2; ++y) {
46677         const int yy0 = y - y0, yy1 = y - y1;
46678         int
46679           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46680           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46681         float
46682           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46683           izM = iz0 + diz02*yy0/dy02,
46684           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
46685           txzM = txz0 + dtxz02*yy0/dy02,
46686           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
46687           tyzM = tyz0 + dtyz02*yy0/dy02,
46688           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
46689           bsM = bs0 + dbs02*yy0/dy02;
46690         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
46691         if (xM>=0 && xm<=w1) {
46692           const int
46693             cxm = cimg::cut(xm,0,w1),
46694             cxM = cimg::cut(xM,0,w1);
46695           T *ptrd = data(cxm,y);
46696           const int dxmM = std::max(1,xM - xm);
46697           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
46698 
46699           for (int x = cxm; x<=cxM; ++x) {
46700             const int xxm = x - xm;
46701             const float
46702               iz = izm + dizmM*xxm/dxmM,
46703               txz = txzm + dtxzmM*xxm/dxmM,
46704               tyz = tyzm + dtyzmM*xxm/dxmM,
46705               cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
46706             const int
46707               tx = (int)cimg::round(txz/iz),
46708               ty = (int)cimg::round(tyz/iz);
46709             const tc *const color = &texture._atXY(tx,ty);
46710             cimg_forC(*this,c) {
46711               const tc col = color[c*twhd];
46712               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
46713               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46714             }
46715             ++ptrd;
46716           }
46717         }
46718       }
46719       return *this;
46720     }
46721 
46722     //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading.
46723     template<typename tz, typename tc>
46724     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
46725                            int x0, int y0, const float z0,
46726                            int x1, int y1, const float z1,
46727                            int x2, int y2, const float z2,
46728                            const CImg<tc>& texture,
46729                            int tx0, int ty0,
46730                            int tx1, int ty1,
46731                            int tx2, int ty2,
46732                            float bs0,
46733                            float bs1,
46734                            float bs2,
46735                            const float opacity=1) {
46736       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46737       if (!is_sameXY(zbuffer))
46738         throw CImgArgumentException(_cimg_instance
46739                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
46740                                     "different dimensions.",
46741                                     cimg_instance,
46742                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
46743       if (texture._depth>1 || texture._spectrum<_spectrum)
46744         throw CImgArgumentException(_cimg_instance
46745                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46746                                     cimg_instance,
46747                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46748       if (is_overlapped(texture))
46749         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,bs0,bs1,bs2,opacity);
46750 
46751       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46752       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
46753       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
46754       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
46755       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46756 
46757       const int
46758         w1 = width() - 1, h1 = height() - 1,
46759         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46760         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46761         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46762         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46763       const float
46764         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
46765         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
46766         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
46767         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
46768         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
46769         dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
46770 
46771       const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
46772       cimg_init_scanline(opacity);
46773 
46774       for (int y = cy0; y<=cy2; ++y) {
46775         const int yy0 = y - y0, yy1 = y - y1;
46776         int
46777           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46778           xM = x0 + (dx02*yy0 + hdy02)/dy02;
46779         float
46780           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
46781           izM = iz0 + diz02*yy0/dy02,
46782           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
46783           txzM = txz0 + dtxz02*yy0/dy02,
46784           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
46785           tyzM = tyz0 + dtyz02*yy0/dy02,
46786           bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
46787           bsM = bs0 + dbs02*yy0/dy02;
46788         if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
46789         if (xM>=0 && xm<=w1) {
46790           const int
46791             cxm = cimg::cut(xm,0,w1),
46792             cxM = cimg::cut(xM,0,w1);
46793           T *ptrd = data(cxm,y);
46794           tz *ptrz = zbuffer.data(cxm,y);
46795           const int dxmM = std::max(1,xM - xm);
46796           const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
46797 
46798           for (int x = cxm; x<=cxM; ++x) {
46799             const int xxm = x - xm;
46800             const float iz = izm + dizmM*xxm/dxmM;
46801             if (iz>=*ptrz) {
46802               *ptrz = (tz)iz;
46803               const float
46804                 txz = txzm + dtxzmM*xxm/dxmM,
46805                 tyz = tyzm + dtyzmM*xxm/dxmM,
46806                 cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
46807               const int
46808                 tx = (int)cimg::round(txz/iz),
46809                 ty = (int)cimg::round(tyz/iz);
46810               const tc *const color = &texture._atXY(tx,ty);
46811               cimg_forC(*this,c) {
46812                 const tc col = color[c*twhd];
46813                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
46814                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46815               }
46816             }
46817             ++ptrd; ++ptrz;
46818           }
46819         }
46820       }
46821       return *this;
46822     }
46823 
46824     //! Draw a textured Phong-shaded 2D triangle.
46825     /**
46826        \param x0 X-coordinate of the first vertex in the image instance.
46827        \param y0 Y-coordinate of the first vertex in the image instance.
46828        \param x1 X-coordinate of the second vertex in the image instance.
46829        \param y1 Y-coordinate of the second vertex in the image instance.
46830        \param x2 X-coordinate of the third vertex in the image instance.
46831        \param y2 Y-coordinate of the third vertex in the image instance.
46832        \param texture Texture image used to fill the triangle.
46833        \param tx0 X-coordinate of the first vertex in the texture image.
46834        \param ty0 Y-coordinate of the first vertex in the texture image.
46835        \param tx1 X-coordinate of the second vertex in the texture image.
46836        \param ty1 Y-coordinate of the second vertex in the texture image.
46837        \param tx2 X-coordinate of the third vertex in the texture image.
46838        \param ty2 Y-coordinate of the third vertex in the texture image.
46839        \param light Light image.
46840        \param lx0 X-coordinate of the first vertex in the light image.
46841        \param ly0 Y-coordinate of the first vertex in the light image.
46842        \param lx1 X-coordinate of the second vertex in the light image.
46843        \param ly1 Y-coordinate of the second vertex in the light image.
46844        \param lx2 X-coordinate of the third vertex in the light image.
46845        \param ly2 Y-coordinate of the third vertex in the light image.
46846        \param opacity Drawing opacity.
46847     **/
46848     template<typename tc, typename tl>
46849     CImg<T>& draw_triangle(int x0, int y0,
46850                            int x1, int y1,
46851                            int x2, int y2,
46852                            const CImg<tc>& texture,
46853                            int tx0, int ty0,
46854                            int tx1, int ty1,
46855                            int tx2, int ty2,
46856                            const CImg<tl>& light,
46857                            int lx0, int ly0,
46858                            int lx1, int ly1,
46859                            int lx2, int ly2,
46860                            const float opacity=1) {
46861       if (is_empty()) return *this;
46862       if (texture._depth>1 || texture._spectrum<_spectrum)
46863         throw CImgArgumentException(_cimg_instance
46864                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46865                                     cimg_instance,
46866                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46867       if (light._depth>1 || light._spectrum<_spectrum)
46868         throw CImgArgumentException(_cimg_instance
46869                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
46870                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
46871       if (is_overlapped(texture))
46872         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46873       if (is_overlapped(light))
46874         return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46875 
46876       if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
46877       if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
46878       if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
46879       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46880 
46881       const int
46882         w1 = width() - 1, h1 = height() - 1,
46883         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46884         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46885         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46886         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
46887         dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
46888         dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
46889         hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
46890         hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2,
46891         dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
46892         dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
46893         hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
46894         hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
46895 
46896       const ulongT
46897         twhd = (ulongT)texture._width*texture._height*texture._depth,
46898         lwhd = (ulongT)light._width*light._height*light._depth;
46899       cimg_init_scanline(opacity);
46900 
46901       for (int y = cy0; y<=cy2; ++y) {
46902         const int yy0 = y - y0, yy1 = y - y1;
46903         int
46904           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
46905           xM = x0 + (dx02*yy0 + hdy02)/dy02,
46906           txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
46907           txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
46908           tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
46909           tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02,
46910           lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
46911           lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
46912           lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
46913           lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
46914         if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,lxm,lxM,lym,lyM);
46915         if (xM>=0 && xm<=w1) {
46916           const int
46917             cxm = cimg::cut(xm,0,w1),
46918             cxM = cimg::cut(xM,0,w1);
46919           T *ptrd = data(cxm,y);
46920           const int
46921             dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
46922             dtxmM = txM - txm, dtymM = tyM - tym,
46923             dlxmM = lxM - lxm, dlymM = lyM - lym;
46924 
46925           for (int x = cxm; x<=cxM; ++x) {
46926             const int
46927               xxm = x - xm,
46928               tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
46929               ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM,
46930               lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
46931               ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
46932             const tc *const color = &texture._atXY(tx,ty);
46933             const tl *const lig = &light._atXY(lx,ly);
46934             cimg_forC(*this,c) {
46935               const tc col = color[c*twhd];
46936               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
46937               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
46938               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
46939             }
46940             ++ptrd;
46941           }
46942         }
46943       }
46944       return *this;
46945     }
46946 
46947     //! Draw a textured Phong-shaded 2D triangle, with perspective correction.
46948     template<typename tc, typename tl>
46949     CImg<T>& draw_triangle(int x0, int y0, const float z0,
46950                            int x1, int y1, const float z1,
46951                            int x2, int y2, const float z2,
46952                            const CImg<tc>& texture,
46953                            int tx0, int ty0,
46954                            int tx1, int ty1,
46955                            int tx2, int ty2,
46956                            const CImg<tl>& light,
46957                            int lx0, int ly0,
46958                            int lx1, int ly1,
46959                            int lx2, int ly2,
46960                            const float opacity=1) {
46961       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
46962       if (texture._depth>1 || texture._spectrum<_spectrum)
46963         throw CImgArgumentException(_cimg_instance
46964                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
46965                                     cimg_instance,
46966                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
46967       if (light._depth>1 || light._spectrum<_spectrum)
46968         throw CImgArgumentException(_cimg_instance
46969                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
46970                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
46971       if (is_overlapped(texture))
46972         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
46973                              light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46974       if (is_overlapped(light))
46975         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,
46976                              +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46977 
46978       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
46979       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
46980       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
46981       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
46982       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
46983 
46984       const int
46985         w1 = width() - 1, h1 = height() - 1,
46986         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
46987         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
46988         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
46989         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
46990       const float
46991         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
46992         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
46993         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
46994         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
46995         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
46996         lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
46997         lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
46998         dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
46999         dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
47000 
47001       const ulongT
47002         twhd = (ulongT)texture._width*texture._height*texture._depth,
47003         lwhd = (ulongT)light._width*light._height*light._depth;
47004       cimg_init_scanline(opacity);
47005 
47006       for (int y = cy0; y<=cy2; ++y) {
47007         const int yy0 = y - y0, yy1 = y - y1;
47008         int
47009           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47010           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47011         float
47012           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47013           izM = iz0 + diz02*yy0/dy02,
47014           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47015           txzM = txz0 + dtxz02*yy0/dy02,
47016           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47017           tyzM = tyz0 + dtyz02*yy0/dy02,
47018           lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
47019           lxzM = lxz0 + dlxz02*yy0/dy02,
47020           lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
47021           lyzM = lyz0 + dlyz02*yy0/dy02;
47022         if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
47023         if (xM>=0 && xm<=w1) {
47024           const int
47025             cxm = cimg::cut(xm,0,w1),
47026             cxM = cimg::cut(xM,0,w1);
47027           T *ptrd = data(cxm,y);
47028           const int dxmM = std::max(1,xM - xm);
47029           const float
47030             dizmM = izM - izm,
47031             dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
47032             dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
47033 
47034           for (int x = cxm; x<=cxM; ++x) {
47035             const int xxm = x - xm;
47036             const float
47037               iz = izm + dizmM*xxm/dxmM,
47038               txz = txzm + dtxzmM*xxm/dxmM,
47039               tyz = tyzm + dtyzmM*xxm/dxmM,
47040               lxz = lxzm + dlxzmM*xxm/dxmM,
47041               lyz = lyzm + dlyzmM*xxm/dxmM;
47042             const int
47043               tx = (int)cimg::round(txz/iz),
47044               ty = (int)cimg::round(tyz/iz),
47045               lx = (int)cimg::round(lxz/iz),
47046               ly = (int)cimg::round(lyz/iz);
47047             const tc *const color = &texture._atXY(tx,ty);
47048             const tl *const lig = &light._atXY(lx,ly);
47049             cimg_forC(*this,c) {
47050               const tc col = color[c*twhd];
47051               const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
47052               const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47053               ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47054             }
47055             ++ptrd;
47056           }
47057         }
47058       }
47059       return *this;
47060     }
47061 
47062     //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering.
47063     template<typename tz, typename tc, typename tl>
47064     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
47065                            int x0, int y0, const float z0,
47066                            int x1, int y1, const float z1,
47067                            int x2, int y2, const float z2,
47068                            const CImg<tc>& texture,
47069                            int tx0, int ty0,
47070                            int tx1, int ty1,
47071                            int tx2, int ty2,
47072                            const CImg<tl>& light,
47073                            int lx0, int ly0,
47074                            int lx1, int ly1,
47075                            int lx2, int ly2,
47076                            const float opacity=1) {
47077       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
47078       if (!is_sameXY(zbuffer))
47079         throw CImgArgumentException(_cimg_instance
47080                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
47081                                     "different dimensions.",
47082                                     cimg_instance,
47083                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
47084       if (texture._depth>1 || texture._spectrum<_spectrum)
47085         throw CImgArgumentException(_cimg_instance
47086                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
47087                                     cimg_instance,
47088                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
47089       if (light._depth>1 || light._spectrum<_spectrum)
47090         throw CImgArgumentException(_cimg_instance
47091                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
47092                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
47093       if (is_overlapped(texture))
47094         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
47095                              +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47096       if (is_overlapped(light))
47097         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
47098                              texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
47099 
47100       float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
47101       if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
47102       if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
47103       if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
47104       if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
47105 
47106       const int
47107         w1 = width() - 1, h1 = height() - 1,
47108         dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
47109         dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
47110         cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
47111         hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
47112       const float
47113         diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
47114         txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
47115         tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
47116         dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
47117         dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
47118         lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
47119         lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
47120         dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
47121         dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
47122 
47123       const ulongT
47124         twhd = (ulongT)texture._width*texture._height*texture._depth,
47125         lwhd = (ulongT)light._width*light._height*light._depth;
47126       cimg_init_scanline(opacity);
47127 
47128       for (int y = cy0; y<=cy2; ++y) {
47129         const int yy0 = y - y0, yy1 = y - y1;
47130         int
47131           xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
47132           xM = x0 + (dx02*yy0 + hdy02)/dy02;
47133         float
47134           izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
47135           izM = iz0 + diz02*yy0/dy02,
47136           txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
47137           txzM = txz0 + dtxz02*yy0/dy02,
47138           tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
47139           tyzM = tyz0 + dtyz02*yy0/dy02,
47140           lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
47141           lxzM = lxz0 + dlxz02*yy0/dy02,
47142           lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
47143           lyzM = lyz0 + dlyz02*yy0/dy02;
47144         if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
47145         if (xM>=0 && xm<=w1) {
47146           const int
47147             cxm = cimg::cut(xm,0,w1),
47148             cxM = cimg::cut(xM,0,w1);
47149           T *ptrd = data(cxm,y);
47150           tz *ptrz = zbuffer.data(cxm,y);
47151           const int dxmM = std::max(1,xM - xm);
47152           const float
47153             dizmM = izM - izm,
47154             dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
47155             dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
47156 
47157           for (int x = cxm; x<=cxM; ++x) {
47158             const int xxm = x - xm;
47159             const float iz = izm + dizmM*xxm/dxmM;
47160             if (iz>=*ptrz) {
47161               *ptrz = (tz)iz;
47162               const float
47163                 txz = txzm + dtxzmM*xxm/dxmM,
47164                 tyz = tyzm + dtyzmM*xxm/dxmM,
47165                 lxz = lxzm + dlxzmM*xxm/dxmM,
47166                 lyz = lyzm + dlyzmM*xxm/dxmM;
47167               const int
47168                 tx = (int)cimg::round(txz/iz),
47169                 ty = (int)cimg::round(tyz/iz),
47170                 lx = (int)cimg::round(lxz/iz),
47171                 ly = (int)cimg::round(lyz/iz);
47172               const tc *const color = &texture._atXY(tx,ty);
47173               const tl *const lig = &light._atXY(lx,ly);
47174               cimg_forC(*this,c) {
47175                 const tc col = color[c*twhd];
47176                 const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
47177                 const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
47178                 ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
47179               }
47180             }
47181             ++ptrd; ++ptrz;
47182           }
47183         }
47184       }
47185       return *this;
47186     }
47187 
47188     //! Draw a filled 4D rectangle.
47189     /**
47190        \param x0 X-coordinate of the upper-left rectangle corner.
47191        \param y0 Y-coordinate of the upper-left rectangle corner.
47192        \param z0 Z-coordinate of the upper-left rectangle corner.
47193        \param c0 C-coordinate of the upper-left rectangle corner.
47194        \param x1 X-coordinate of the lower-right rectangle corner.
47195        \param y1 Y-coordinate of the lower-right rectangle corner.
47196        \param z1 Z-coordinate of the lower-right rectangle corner.
47197        \param c1 C-coordinate of the lower-right rectangle corner.
47198        \param val Scalar value used to fill the rectangle area.
47199        \param opacity Drawing opacity.
47200     **/
47201     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
47202                             const int x1, const int y1, const int z1, const int c1,
47203                             const T val, const float opacity=1) {
47204       if (is_empty()) return *this;
47205       const int
47206         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
47207         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
47208         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
47209         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
47210       const int
47211         lx = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
47212         ly = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
47213         lz = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
47214         lc = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
47215       const ulongT
47216         offX = (ulongT)_width - lx,
47217         offY = (ulongT)_width*(_height - ly),
47218         offZ = (ulongT)_width*_height*(_depth - lz);
47219       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
47220       T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
47221       if (lx>0 && ly>0 && lz>0 && lc>0)
47222         for (int v = 0; v<lc; ++v) {
47223           for (int z = 0; z<lz; ++z) {
47224             for (int y = 0; y<ly; ++y) {
47225               if (opacity>=1) {
47226                 if (sizeof(T)!=1) { for (int x = 0; x<lx; ++x) *(ptrd++) = val; ptrd+=offX; }
47227                 else { std::memset(ptrd,(int)val,lx); ptrd+=_width; }
47228               } else { for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
47229             }
47230             ptrd+=offY;
47231           }
47232           ptrd+=offZ;
47233         }
47234       return *this;
47235     }
47236 
47237     //! Draw a filled 3D rectangle.
47238     /**
47239        \param x0 X-coordinate of the upper-left rectangle corner.
47240        \param y0 Y-coordinate of the upper-left rectangle corner.
47241        \param z0 Z-coordinate of the upper-left rectangle corner.
47242        \param x1 X-coordinate of the lower-right rectangle corner.
47243        \param y1 Y-coordinate of the lower-right rectangle corner.
47244        \param z1 Z-coordinate of the lower-right rectangle corner.
47245        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
47246        \param opacity Drawing opacity.
47247     **/
47248     template<typename tc>
47249     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
47250                             const int x1, const int y1, const int z1,
47251                             const tc *const color, const float opacity=1) {
47252       if (is_empty()) return *this;
47253       if (!color)
47254         throw CImgArgumentException(_cimg_instance
47255                                     "draw_rectangle(): Specified color is (null).",
47256                                     cimg_instance);
47257       cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
47258       return *this;
47259     }
47260 
47261     //! Draw a filled 2D rectangle.
47262     /**
47263        \param x0 X-coordinate of the upper-left rectangle corner.
47264        \param y0 Y-coordinate of the upper-left rectangle corner.
47265        \param x1 X-coordinate of the lower-right rectangle corner.
47266        \param y1 Y-coordinate of the lower-right rectangle corner.
47267        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
47268        \param opacity Drawing opacity.
47269     **/
47270     template<typename tc>
47271     CImg<T>& draw_rectangle(const int x0, const int y0,
47272                             const int x1, const int y1,
47273                             const tc *const color, const float opacity=1) {
47274       return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity);
47275     }
47276 
47277     //! Draw a outlined 2D rectangle \overloading.
47278     template<typename tc>
47279     CImg<T>& draw_rectangle(const int x0, const int y0,
47280                             const int x1, const int y1,
47281                             const tc *const color, const float opacity,
47282                             const unsigned int pattern) {
47283       if (is_empty()) return *this;
47284       if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
47285       if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
47286       const int
47287         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
47288         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0;
47289       if (ny1==ny0 + 1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
47290                       draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
47291       return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
47292         draw_line(nx1,ny0 + 1,nx1,ny1 - 1,color,opacity,pattern,false).
47293         draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
47294         draw_line(nx0,ny1 - 1,nx0,ny0 + 1,color,opacity,pattern,false);
47295     }
47296 
47297     //! Draw a filled 2D polygon.
47298     /**
47299        \param points Set of polygon vertices.
47300        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
47301        \param opacity Drawing opacity.
47302      **/
47303     template<typename tp, typename tc>
47304     CImg<T>& draw_polygon(const CImg<tp>& points,
47305                           const tc *const color, const float opacity=1) {
47306       if (is_empty() || !points) return *this;
47307       if (!color)
47308         throw CImgArgumentException(_cimg_instance
47309                                     "draw_polygon(): Specified color is (null).",
47310                                     cimg_instance);
47311       if (points.height()!=2)
47312         throw CImgArgumentException(_cimg_instance
47313                                     "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
47314                                     cimg_instance,
47315                                     points._width,points._height,points._depth,points._spectrum);
47316       if (points._width==1) return draw_point(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),color,opacity);
47317       if (points._width==2) return draw_line(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),
47318                                              cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),color,opacity);
47319       if (points._width==3) return draw_triangle(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),
47320                                                  cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),
47321                                                  cimg::uiround(points(2,0)),cimg::uiround(points(2,1)),color,opacity);
47322       cimg_init_scanline(opacity);
47323       int
47324         xmin = 0, ymin = 0,
47325         xmax = points.get_shared_row(0).max_min(xmin),
47326         ymax = points.get_shared_row(1).max_min(ymin);
47327       if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
47328       if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity);
47329 
47330       ymin = std::max(0,ymin);
47331       ymax = std::min(height() - 1,ymax);
47332       CImg<intT> Xs(points._width,ymax - ymin + 1);
47333       CImg<uintT> count(Xs._height,1,1,1,0);
47334       unsigned int n = 0, nn = 1;
47335       bool go_on = true;
47336 
47337       while (go_on) {
47338         unsigned int an = (nn + 1)%points._width;
47339         const int
47340           x0 = cimg::uiround(points(n,0)),
47341           y0 = cimg::uiround(points(n,1));
47342         if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; }
47343         const int
47344           x1 = cimg::uiround(points(nn,0)),
47345           y1 = cimg::uiround(points(nn,1));
47346         unsigned int tn = an;
47347         while (points(tn,1)==y1) (tn+=1)%=points._width;
47348 
47349         if (y0!=y1) {
47350           const int
47351             y2 = cimg::uiround(points(tn,1)),
47352             x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1,
47353             step = cimg::sign(y01),
47354             tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2,
47355             tend = tmax - (step==cimg::sign(y12));
47356           unsigned int y = (unsigned int)y0 - ymin;
47357           for (int t = 0; t<=tend; ++t, y+=step)
47358             if (y<Xs._height) Xs(count[y]++,y) = x0 + (t*x01 + htmax)/tmax;
47359         }
47360         go_on = nn>n;
47361         n = nn;
47362         nn = an;
47363       }
47364 
47365       cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*512))
47366       cimg_forY(Xs,y) {
47367         const CImg<intT> Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort();
47368         int px = width();
47369         for (unsigned int k = 0; k<Xsy._width; k+=2) {
47370           int x0 = Xsy[k];
47371           const int x1 = Xsy[k + 1];
47372           x0+=x0==px;
47373           cimg_draw_scanline(x0,x1,y + ymin,color,opacity,1);
47374           px = x1;
47375         }
47376       }
47377       return *this;
47378     }
47379 
47380     //! Draw a outlined 2D or 3D polygon \overloading.
47381     template<typename t, typename tc>
47382     CImg<T>& draw_polygon(const CImg<t>& points,
47383                           const tc *const color, const float opacity, const unsigned int pattern) {
47384       if (is_empty() || !points) return *this;
47385       if (!color)
47386         throw CImgArgumentException(_cimg_instance
47387                                     "draw_polygon(): Specified color is (null).",
47388                                     cimg_instance);
47389       if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity);
47390       if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1),
47391                                              (int)points(1,0),(int)points(1,1),color,opacity,pattern);
47392       bool ninit_hatch = true;
47393       switch (points._height) {
47394       case 0 : case 1 :
47395         throw CImgArgumentException(_cimg_instance
47396                                     "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
47397                                     cimg_instance,
47398                                     points._width,points._height,points._depth,points._spectrum);
47399       default : {
47400         CImg<intT> npoints(points._width,2);
47401         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
47402         unsigned int nb_points = 1;
47403         for (unsigned int p = 1; p<points._width; ++p) {
47404           const int nx = (int)points(p,0), ny = (int)points(p,1);
47405           if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
47406         }
47407         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
47408         int ox = x0, oy = y0;
47409         for (unsigned int i = 1; i<nb_points; ++i) {
47410           const int _x = (int)npoints(i,0), _y = (int)npoints(i,1);
47411           draw_line(ox,oy,_x,_y,color,opacity,pattern,ninit_hatch);
47412           ninit_hatch = false;
47413           ox = _x; oy = _y;
47414         }
47415         draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
47416       }
47417       }
47418       return *this;
47419     }
47420 
47421     //! Draw a filled 2D ellipse.
47422     /**
47423        \param x0 X-coordinate of the ellipse center.
47424        \param y0 Y-coordinate of the ellipse center.
47425        \param r1 First radius of the ellipse.
47426        \param r2 Second radius of the ellipse.
47427        \param angle Angle of the first radius.
47428        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47429        \param opacity Drawing opacity.
47430     **/
47431     template<typename tc>
47432     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
47433                           const tc *const color, const float opacity=1) {
47434       return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U,true);
47435     }
47436 
47437     //! Draw a filled 2D ellipse \overloading.
47438     /**
47439        \param x0 X-coordinate of the ellipse center.
47440        \param y0 Y-coordinate of the ellipse center.
47441        \param tensor Diffusion tensor describing the ellipse.
47442        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47443        \param opacity Drawing opacity.
47444     **/
47445     template<typename t, typename tc>
47446     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
47447                           const tc *const color, const float opacity=1) {
47448       CImgList<t> eig = tensor.get_symmetric_eigen();
47449       const CImg<t> &val = eig[0], &vec = eig[1];
47450       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
47451                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
47452                           color,opacity);
47453     }
47454 
47455     //! Draw an outlined 2D ellipse.
47456     /**
47457        \param x0 X-coordinate of the ellipse center.
47458        \param y0 Y-coordinate of the ellipse center.
47459        \param r1 First radius of the ellipse.
47460        \param r2 Second radius of the ellipse.
47461        \param angle Angle of the first radius.
47462        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47463        \param opacity Drawing opacity.
47464        \param pattern An integer whose bits describe the outline pattern.
47465     **/
47466     template<typename tc>
47467     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
47468                           const tc *const color, const float opacity, const unsigned int pattern) {
47469       if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern,false);
47470       return *this;
47471     }
47472 
47473     //! Draw an outlined 2D ellipse \overloading.
47474     /**
47475        \param x0 X-coordinate of the ellipse center.
47476        \param y0 Y-coordinate of the ellipse center.
47477        \param tensor Diffusion tensor describing the ellipse.
47478        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47479        \param opacity Drawing opacity.
47480        \param pattern An integer whose bits describe the outline pattern.
47481     **/
47482     template<typename t, typename tc>
47483     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
47484                           const tc *const color, const float opacity,
47485                           const unsigned int pattern) {
47486       CImgList<t> eig = tensor.get_symmetric_eigen();
47487       const CImg<t> &val = eig[0], &vec = eig[1];
47488       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
47489                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
47490                           color,opacity,pattern);
47491     }
47492 
47493     template<typename tc>
47494     CImg<T>& _draw_ellipse(const int x0, const int y0, const float radius1, const float radius2, const float angle,
47495                            const tc *const color, const float opacity,
47496                            const unsigned int pattern, const bool is_filled) {
47497       if (is_empty() || (!is_filled && !pattern)) return *this;
47498       const float radiusM = std::max(radius1,radius2);
47499       if (radius1<0 || radius2<0 || x0 - radiusM>=width() || y0 + radiusM<0 || y0 - radiusM>=height()) return *this;
47500       if (!color)
47501         throw CImgArgumentException(_cimg_instance
47502                                     "draw_ellipse(): Specified color is (null).",
47503                                     cimg_instance);
47504       const int iradius1 = (int)cimg::round(radius1), iradius2 = (int)cimg::round(radius2);
47505       if (!iradius1 && !iradius2) return draw_point(x0,y0,color,opacity);
47506       if (iradius1==iradius2) {
47507         if (is_filled) return draw_circle(x0,y0,iradius1,color,opacity);
47508         else if (pattern==~0U) return draw_circle(x0,y0,iradius1,color,opacity,pattern);
47509       }
47510       const float ang = (float)(angle*cimg::PI/180);
47511 
47512       if (!is_filled) { // Outlined
47513         const float ca = std::cos(ang), sa = std::sin(ang);
47514         CImg<int> points((unsigned int)cimg::round(6*radiusM),2);
47515         cimg_forX(points,k) {
47516           const float
47517             _ang = (float)(2*cimg::PI*k/points._width),
47518             X = (float)(radius1*std::cos(_ang)),
47519             Y = (float)(radius2*std::sin(_ang));
47520           points(k,0) = (int)cimg::round(x0 + (X*ca - Y*sa));
47521           points(k,1) = (int)cimg::round(y0 + (X*sa + Y*ca));
47522         }
47523         draw_polygon(points,color,opacity,pattern);
47524       } else { // Filled
47525         cimg_init_scanline(opacity);
47526         const float
47527           ca = std::cos(ang),
47528           sa = -std::sin(ang),
47529           ca2 = ca*ca,
47530           sa2 = sa*sa,
47531           casa = ca*sa,
47532           i1 = 1/cimg::sqr(radius1),
47533           i2 = 1/cimg::sqr(radius2),
47534           t1 = i1*ca2 + i2*sa2,
47535           t2 = (i2 - i1)*casa,
47536           t3 = i2*ca2 + i1*sa2,
47537           t12 = t1*2;
47538         const int
47539           _ymin = (int)std::floor(y0 - radiusM),
47540           _ymax = (int)std::ceil(y0 + radiusM),
47541           ymin = _ymin<0?0:_ymin,
47542           ymax = _ymax>=height()?height() - 1:_ymax;
47543         for (int y = ymin; y<=ymax; ++y) {
47544           const float
47545             Y = y - y0 + 0.5f,
47546             B = 2*t2*Y,
47547             C = t3*Y*Y - 1,
47548             D = B*B - 4*t1*C;
47549           if (D>=0) {
47550             const float sD = std::sqrt(D);
47551             const int
47552               xmin = (int)(x0 + cimg::round((-B - sD)/t12)),
47553               xmax = (int)(x0 + cimg::round((-B + sD)/t12));
47554             cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
47555           }
47556         }
47557       }
47558       return *this;
47559     }
47560 
47561     //! Draw a filled 2D circle.
47562     /**
47563        \param x0 X-coordinate of the circle center.
47564        \param y0 Y-coordinate of the circle center.
47565        \param radius  Circle radius.
47566        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47567        \param opacity Drawing opacity.
47568        \note
47569        - Circle version of the Bresenham's algorithm is used.
47570     **/
47571     template<typename tc>
47572     CImg<T>& draw_circle(const int x0, const int y0, int radius,
47573                          const tc *const color, const float opacity=1) {
47574       if (is_empty()) return *this;
47575       if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
47576       if (!color)
47577         throw CImgArgumentException(_cimg_instance
47578                                     "draw_circle(): Specified color is (null).",
47579                                     cimg_instance);
47580       if (!radius) return draw_point(x0,y0,color,opacity);
47581       cimg_init_scanline(opacity);
47582       if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1);
47583       for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
47584         if (f>=0) {
47585           const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y;
47586           if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
47587           if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
47588           f+=(ddFy+=2); --y;
47589         }
47590         const bool no_diag = y!=(x++);
47591         ++(f+=(ddFx+=2));
47592         const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x;
47593         if (no_diag) {
47594           if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
47595           if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
47596         }
47597       }
47598       return *this;
47599     }
47600 
47601     //! Draw an outlined 2D circle.
47602     /**
47603        \param x0 X-coordinate of the circle center.
47604        \param y0 Y-coordinate of the circle center.
47605        \param radius Circle radius.
47606        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
47607        \param opacity Drawing opacity.
47608        \param pattern An integer whose bits describe the outline pattern.
47609     **/
47610     template<typename tc>
47611     CImg<T>& draw_circle(const int x0, const int y0, int radius,
47612                          const tc *const color, const float opacity,
47613                          const unsigned int pattern) {
47614       if (pattern!=~0U) return draw_ellipse(x0,y0,radius,radius,0,color,opacity,pattern);
47615       if (is_empty()) return *this;
47616       if (!color)
47617         throw CImgArgumentException(_cimg_instance
47618                                     "draw_circle(): Specified color is (null).",
47619                                     cimg_instance);
47620       if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
47621       if (!radius) return draw_point(x0,y0,color,opacity);
47622 
47623       draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity).
47624         draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity);
47625       if (radius==1) return *this;
47626       for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
47627         if (f>=0) { f+=(ddFy+=2); --y; }
47628         ++x; ++(f+=(ddFx+=2));
47629         if (x!=y + 1) {
47630           const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x,
47631             x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y;
47632           draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
47633             draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
47634           if (x!=y)
47635             draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
47636               draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
47637         }
47638       }
47639       return *this;
47640     }
47641 
47642     //! Draw an image.
47643     /**
47644        \param sprite Sprite image.
47645        \param x0 X-coordinate of the sprite position.
47646        \param y0 Y-coordinate of the sprite position.
47647        \param z0 Z-coordinate of the sprite position.
47648        \param c0 C-coordinate of the sprite position.
47649        \param opacity Drawing opacity.
47650     **/
47651     template<typename t>
47652     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
47653                         const CImg<t>& sprite, const float opacity=1) {
47654       if (is_empty() || !sprite) return *this;
47655       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
47656       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
47657         return assign(sprite,false);
47658       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
47659       const int
47660         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
47661         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
47662         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
47663         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
47664         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
47665         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
47666 
47667       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
47668       if (lx>0 && ly>0 && lz>0 && lc>0) {
47669         for (int c = 0; c<lc; ++c)
47670           for (int z = 0; z<lz; ++z)
47671             for (int y = 0; y<ly; ++y) {
47672               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
47673               const t *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
47674               if (opacity>=1) for (int x = 0; x<lx; ++x) *(ptrd++) = (T)*(ptrs++);
47675               else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
47676             }
47677       }
47678       return *this;
47679     }
47680 
47681     //! Draw an image \specialization.
47682     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
47683                         const CImg<T>& sprite, const float opacity=1) {
47684       if (is_empty() || !sprite) return *this;
47685       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
47686       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
47687         return assign(sprite,false);
47688       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
47689       const int
47690         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
47691         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
47692         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
47693         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
47694         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
47695         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
47696       const ulongT slx = lx*sizeof(T);
47697 
47698       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
47699       if (lx>0 && ly>0 && lz>0 && lc>0) {
47700         for (int c = 0; c<lc; ++c)
47701           for (int z = 0; z<lz; ++z)
47702             for (int y = 0; y<ly; ++y) {
47703               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
47704               const T *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
47705               if (opacity>=1) std::memcpy(ptrd,ptrs,slx);
47706               else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
47707             }
47708       }
47709       return *this;
47710     }
47711 
47712     //! Draw an image \overloading.
47713     template<typename t>
47714     CImg<T>& draw_image(const int x0, const int y0, const int z0,
47715                         const CImg<t>& sprite, const float opacity=1) {
47716       return draw_image(x0,y0,z0,0,sprite,opacity);
47717     }
47718 
47719     //! Draw an image \overloading.
47720     template<typename t>
47721     CImg<T>& draw_image(const int x0, const int y0,
47722                         const CImg<t>& sprite, const float opacity=1) {
47723       return draw_image(x0,y0,0,sprite,opacity);
47724     }
47725 
47726     //! Draw an image \overloading.
47727     template<typename t>
47728     CImg<T>& draw_image(const int x0,
47729                         const CImg<t>& sprite, const float opacity=1) {
47730       return draw_image(x0,0,sprite,opacity);
47731     }
47732 
47733     //! Draw an image \overloading.
47734     template<typename t>
47735     CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
47736       return draw_image(0,sprite,opacity);
47737     }
47738 
47739     //! Draw a masked image.
47740     /**
47741        \param sprite Sprite image.
47742        \param mask Mask image.
47743        \param x0 X-coordinate of the sprite position in the image instance.
47744        \param y0 Y-coordinate of the sprite position in the image instance.
47745        \param z0 Z-coordinate of the sprite position in the image instance.
47746        \param c0 C-coordinate of the sprite position in the image instance.
47747        \param mask_max_value Maximum pixel value of the mask image \c mask.
47748        \param opacity Drawing opacity.
47749        \note
47750        - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
47751        - Dimensions along x,y and z of \p sprite and \p mask must be the same.
47752     **/
47753     template<typename ti, typename tm>
47754     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
47755                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
47756                         const float mask_max_value=1) {
47757       if (is_empty() || !sprite || !mask) return *this;
47758       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
47759       if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
47760       if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
47761         throw CImgArgumentException(_cimg_instance
47762                                     "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have "
47763                                     "incompatible dimensions.",
47764                                     cimg_instance,
47765                                     sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
47766                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
47767 
47768       const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
47769       const int
47770         dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
47771         sx0 = dx0 - x0,  sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
47772         lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
47773         ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
47774         lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
47775         lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
47776       const ulongT msize = mask.size();
47777 
47778       if (lx>0 && ly>0 && lz>0 && lc>0) {
47779         for (int c = 0; c<lc; ++c)
47780           for (int z = 0; z<lz; ++z)
47781             for (int y = 0; y<ly; ++y) {
47782               T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
47783               const ti *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
47784               const tm *ptrm = mask._data + (mask.offset(sx0,sy0 + y,sz0 + z,sc0 + c)%msize);
47785               for (int x = 0; x<lx; ++x) {
47786                 const float mopacity = (float)(*(ptrm++)*opacity),
47787                   nopacity = cimg::abs(mopacity), copacity = mask_max_value - std::max(mopacity,0.f);
47788                 *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
47789                 ++ptrd;
47790               }
47791             }
47792       }
47793       return *this;
47794     }
47795 
47796     //! Draw a masked image \overloading.
47797     template<typename ti, typename tm>
47798     CImg<T>& draw_image(const int x0, const int y0, const int z0,
47799                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
47800                         const float mask_max_value=1) {
47801       return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
47802     }
47803 
47804     //! Draw a image \overloading.
47805     template<typename ti, typename tm>
47806     CImg<T>& draw_image(const int x0, const int y0,
47807                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
47808                         const float mask_max_value=1) {
47809       return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
47810     }
47811 
47812     //! Draw a image \overloading.
47813     template<typename ti, typename tm>
47814     CImg<T>& draw_image(const int x0,
47815                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
47816                         const float mask_max_value=1) {
47817       return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
47818     }
47819 
47820     //! Draw an image.
47821     template<typename ti, typename tm>
47822     CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
47823                         const float mask_max_value=1) {
47824       return draw_image(0,sprite,mask,opacity,mask_max_value);
47825     }
47826 
47827     //! Draw a text string.
47828     /**
47829        \param x0 X-coordinate of the text in the image instance.
47830        \param y0 Y-coordinate of the text in the image instance.
47831        \param text Format of the text ('printf'-style format string).
47832        \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color.
47833        \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color.
47834        \param opacity Drawing opacity.
47835        \param font Font used for drawing text.
47836     **/
47837     template<typename tc1, typename tc2, typename t>
47838     CImg<T>& draw_text(const int x0, const int y0,
47839                        const char *const text,
47840                        const tc1 *const foreground_color, const tc2 *const background_color,
47841                        const float opacity, const CImgList<t>& font, ...) {
47842       if (!font) return *this;
47843       CImg<charT> tmp(2048);
47844       std::va_list ap; va_start(ap,font);
47845       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
47846       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false);
47847     }
47848 
47849     //! Draw a text string \overloading.
47850     /**
47851        \note A transparent background is used for the text.
47852     **/
47853     template<typename tc, typename t>
47854     CImg<T>& draw_text(const int x0, const int y0,
47855                        const char *const text,
47856                        const tc *const foreground_color, const int,
47857                        const float opacity, const CImgList<t>& font, ...) {
47858       if (!font) return *this;
47859       CImg<charT> tmp(2048);
47860       std::va_list ap; va_start(ap,font);
47861       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
47862       return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false);
47863     }
47864 
47865     //! Draw a text string \overloading.
47866     /**
47867        \note A transparent foreground is used for the text.
47868     **/
47869     template<typename tc, typename t>
47870     CImg<T>& draw_text(const int x0, const int y0,
47871                        const char *const text,
47872                        const int, const tc *const background_color,
47873                        const float opacity, const CImgList<t>& font, ...) {
47874       if (!font) return *this;
47875       CImg<charT> tmp(2048);
47876       std::va_list ap; va_start(ap,font);
47877       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
47878       return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false);
47879     }
47880 
47881     //! Draw a text string \overloading.
47882     /**
47883        \param x0 X-coordinate of the text in the image instance.
47884        \param y0 Y-coordinate of the text in the image instance.
47885        \param text Format of the text ('printf'-style format string).
47886        \param foreground_color Array of spectrum() values of type \c T,
47887          defining the foreground color (0 means 'transparent').
47888        \param background_color Array of spectrum() values of type \c T,
47889          defining the background color (0 means 'transparent').
47890        \param opacity Drawing opacity.
47891        \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise).
47892     **/
47893     template<typename tc1, typename tc2>
47894     CImg<T>& draw_text(const int x0, const int y0,
47895                        const char *const text,
47896                        const tc1 *const foreground_color, const tc2 *const background_color,
47897                        const float opacity=1, const unsigned int font_height=13, ...) {
47898       if (!font_height) return *this;
47899       CImg<charT> tmp(2048);
47900       std::va_list ap; va_start(ap,font_height);
47901       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
47902       const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true);
47903       _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true);
47904       return *this;
47905     }
47906 
47907     //! Draw a text string \overloading.
47908     template<typename tc>
47909     CImg<T>& draw_text(const int x0, const int y0,
47910                        const char *const text,
47911                        const tc *const foreground_color, const int background_color=0,
47912                        const float opacity=1, const unsigned int font_height=13, ...) {
47913       if (!font_height) return *this;
47914       cimg::unused(background_color);
47915       CImg<charT> tmp(2048);
47916       std::va_list ap; va_start(ap,font_height);
47917       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
47918       return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data);
47919     }
47920 
47921     //! Draw a text string \overloading.
47922     template<typename tc>
47923     CImg<T>& draw_text(const int x0, const int y0,
47924                        const char *const text,
47925                        const int, const tc *const background_color,
47926                        const float opacity=1, const unsigned int font_height=13, ...) {
47927       if (!font_height) return *this;
47928       CImg<charT> tmp(2048);
47929       std::va_list ap; va_start(ap,font_height);
47930       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
47931       return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data);
47932     }
47933 
47934     template<typename tc1, typename tc2, typename t>
47935     CImg<T>& _draw_text(const int x0, const int y0,
47936                         const char *const text,
47937                         const tc1 *const foreground_color, const tc2 *const background_color,
47938                         const float opacity, const CImgList<t>& font,
47939                         const bool is_native_font) {
47940       if (!text) return *this;
47941       if (!font)
47942         throw CImgArgumentException(_cimg_instance
47943                                     "draw_text(): Empty specified font.",
47944                                     cimg_instance);
47945 
47946       const unsigned int text_length = (unsigned int)std::strlen(text);
47947       const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4;
47948       unsigned char o_ch, ch = 0;
47949       int x, y, w;
47950       CImg<intT> left_paddings(text_length,1,1,1,0);
47951       const CImg<t> empty = CImg<t>::empty();
47952 
47953       if (is_empty() || is_native_font) {
47954         // Pre-compute necessary size of the image as well as left paddings of each character.
47955         x = y = w = 0;
47956         o_ch = 0;
47957         for (unsigned int i = 0; i<text_length; ++i) {
47958           ch = (unsigned char)text[i];
47959           switch (ch) {
47960           case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
47961           case '\t' : x+=4*font[(int)' ']._width; break;
47962           case ' ' : x+=font[(int)' ']._width; break;
47963           default : if (ch<font._width) {
47964               int left_padding = 0;
47965               if (is_native_font && font[0]._height<128) {
47966                 // Determine left padding from various rules.
47967                 if (ch==':' || ch=='!' || ch=='.' || ch==';')
47968                   left_padding = 2*padding_x;
47969                 else if (o_ch==',' || (o_ch=='.' && (ch<'0' || ch>'9')) || o_ch==';' || o_ch==':' || o_ch=='!')
47970                   left_padding = 4*padding_x;
47971                 else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') &&
47972                           ((ch>='0' && ch<='9') ||
47973                            (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') ||
47974                            (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) ||
47975                          o_ch=='.' || o_ch=='\'' || ch=='\'')
47976                   left_padding = padding_x;
47977                 else if ((o_ch<'0' || o_ch>'9') && ch!='-') {
47978                   const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
47979                   if (o_ch && ch>' ' && o_ch>' ' && mask._height>13) {
47980                     const CImg<t> &o_mask = o_ch + 256U<font._width?font[o_ch + 256]:empty;
47981                     if (o_mask._height>13) {
47982                       const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0;
47983                       left_padding = -10;
47984                       cimg_forY(mask,k) {
47985                         const int
47986                           lpad = o_mask(w1,k)>=8?0:
47987                                  o_mask._width<=2 || o_mask(w2,k)>=8?-1:
47988                                  o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3,
47989                           rpad = mask(0,k)>=8?0:
47990                                  mask._width<=2 || mask(1,k)>=8?-1:
47991                                  mask._width<=3 || mask(2,k)>=8?-2:-3;
47992                         left_padding = std::max(left_padding,lpad + rpad);
47993                       }
47994                     }
47995                   }
47996                 }
47997                 left_paddings[i] = left_padding;
47998               }
47999               x+=left_padding + font[ch]._width + padding_x;
48000               o_ch = ch;
48001             }
48002           }
48003         }
48004         if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; }
48005         if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
48006       }
48007 
48008       // Draw font characters on image.
48009       x = x0; y = y0;
48010       for (unsigned int i = 0; i<text_length; ++i) {
48011         ch = (unsigned char)text[i];
48012         switch (ch) {
48013         case '\n' : y+=font[0]._height; x = x0; break;
48014         case '\t' : x+=4*font[(int)' ']._width; break;
48015         case ' ' : x+=font[(int)' ']._width; break;
48016         default : if (ch<font._width) {
48017             CImg<T> letter = font[ch];
48018             if (letter) {
48019               const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
48020               const int posx = x + left_paddings[i] + padding_x;
48021               if (is_native_font && _spectrum>letter._spectrum)
48022                 letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false);
48023               const unsigned int cmin = std::min(_spectrum,letter._spectrum);
48024               if (foreground_color)
48025                 for (unsigned int c = 0; c<cmin; ++c)
48026                   if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c];
48027               if (mask) { // Letter has mask
48028                 if (background_color)
48029                   for (unsigned int c = 0; c<cmin; ++c)
48030                     draw_rectangle(x,y,0,c,posx + letter._width - 1,y + letter._height - 1,0,c,
48031                                    background_color[c],opacity);
48032                 draw_image(posx,y,letter,font[ch + 256],opacity,255.f);
48033               } else draw_image(posx,y,letter,opacity); // Letter has no mask
48034               x = posx + letter._width;
48035             }
48036           }
48037         }
48038       }
48039       return *this;
48040     }
48041 
48042     // [internal] Version used to display text in interactive viewers.
48043     CImg<T>& __draw_text(const char *const text, unsigned int &font_size, const int is_down, ...) {
48044       CImg<charT> tmp(2048);
48045       std::va_list ap;
48046       va_start(ap,is_down);
48047       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
48048       CImg<ucharT> a_label, a_labelmask;
48049       const unsigned char a_labelcolor = 255;
48050       unsigned int ofs = font_size, fs = ofs;
48051       do { // Determine best font size
48052         a_label.assign().draw_text(0,0,"%s",&a_labelcolor,0,1,fs,tmp._data);
48053         if (a_label._width<7*_width/10 && a_label._height>_height/20 && a_label._height<_height/5) {
48054           font_size = fs; break;
48055         } else if ((a_label._width>7*_width/10 || a_label._height>_height/5) && fs>13 && ofs>=fs) {
48056           ofs = fs; fs = std::max(13U,(unsigned int)cimg::round(fs/1.25f));
48057         } else if (a_label._width<3*_width/10 && a_label._height<_height/20 && fs<64 && ofs<=fs) {
48058           ofs = fs; fs = std::min(64U,(unsigned int)cimg::round(fs*1.25f));
48059         } else { font_size = fs; break; }
48060       } while (true);
48061       a_label.normalize(0,255);
48062       a_label+=(255 - a_label.get_dilate(3)).normalize(0,80);
48063       a_label.resize(-100,-100,1,3,1);
48064       return draw_image(0,is_down?height() - a_label.height():0,a_label,0.85f);
48065     }
48066 
48067     //! Draw a 2D vector field.
48068     /**
48069        \param flow Image of 2D vectors used as input data.
48070        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48071        \param opacity Drawing opacity.
48072        \param sampling Length (in pixels) between each arrow.
48073        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
48074        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
48075        \param pattern Used pattern to draw lines.
48076        \note Clipping is supported.
48077     **/
48078     template<typename t1, typename t2>
48079     CImg<T>& draw_quiver(const CImg<t1>& flow,
48080                          const t2 *const color, const float opacity=1,
48081                          const unsigned int sampling=25, const float factor=-20,
48082                          const bool is_arrow=true, const unsigned int pattern=~0U) {
48083       return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
48084     }
48085 
48086     //! Draw a 2D vector field, using a field of colors.
48087     /**
48088        \param flow Image of 2D vectors used as input data.
48089        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
48090        \param opacity Opacity of the drawing.
48091        \param sampling Length (in pixels) between each arrow.
48092        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
48093        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
48094        \param pattern Used pattern to draw lines.
48095        \note Clipping is supported.
48096     **/
48097     template<typename t1, typename t2>
48098     CImg<T>& draw_quiver(const CImg<t1>& flow,
48099                          const CImg<t2>& color, const float opacity=1,
48100                          const unsigned int sampling=25, const float factor=-20,
48101                          const bool is_arrow=true, const unsigned int pattern=~0U) {
48102       if (is_empty()) return *this;
48103       if (!flow || flow._spectrum!=2)
48104         throw CImgArgumentException(_cimg_instance
48105                                     "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
48106                                     cimg_instance,
48107                                     flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
48108       if (sampling<=0)
48109         throw CImgArgumentException(_cimg_instance
48110                                     "draw_quiver(): Invalid sampling value %g "
48111                                     "(should be >0)",
48112                                     cimg_instance,
48113                                     sampling);
48114       const bool colorfield = (color._width==flow._width && color._height==flow._height &&
48115                                color._depth==1 && color._spectrum==_spectrum);
48116       if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
48117       float vmax,fact;
48118       if (factor<=0) {
48119         float m, M = (float)flow.get_norm(2).max_min(m);
48120         vmax = (float)std::max(cimg::abs(m),cimg::abs(M));
48121         if (!vmax) vmax = 1;
48122         fact = -factor;
48123       } else { fact = factor; vmax = 1; }
48124 
48125       for (unsigned int y = sampling/2; y<_height; y+=sampling)
48126         for (unsigned int x = sampling/2; x<_width; x+=sampling) {
48127           const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
48128           float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
48129           if (is_arrow) {
48130             const int xx = (int)(x + u), yy = (int)(y + v);
48131             if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern);
48132             else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern);
48133           } else {
48134             if (colorfield)
48135               draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
48136                         color.get_vector_at(X,Y)._data,opacity,pattern);
48137             else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
48138                            color._data,opacity,pattern);
48139           }
48140         }
48141       return *this;
48142     }
48143 
48144     //! Draw a labeled horizontal axis.
48145     /**
48146        \param values_x Values along the horizontal axis.
48147        \param y Y-coordinate of the horizontal axis in the image instance.
48148        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48149        \param opacity Drawing opacity.
48150        \param pattern Drawing pattern.
48151        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
48152        \param allow_zero Enable/disable the drawing of label '0' if found.
48153     **/
48154     template<typename t, typename tc>
48155     CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
48156                        const tc *const color, const float opacity=1,
48157                        const unsigned int pattern=~0U, const unsigned int font_height=13,
48158                        const bool allow_zero=true, const float round_x=0) {
48159       if (is_empty()) return *this;
48160       const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height;
48161       const int siz = (int)values_x.size() - 1;
48162       CImg<charT> txt(32);
48163       CImg<T> a_label;
48164       if (siz<=0) { // Degenerated case
48165         draw_line(0,y,_width - 1,y,color,opacity,pattern);
48166         if (!siz) {
48167           cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)*values_x,round_x):(double)*values_x);
48168           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
48169           const int
48170             _xt = (width() - a_label.width())/2,
48171             xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
48172           draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity);
48173           if (allow_zero || *txt!='0' || txt[1]!=0)
48174             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
48175         }
48176       } else { // Regular case
48177         if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width - 1,y,color,opacity,30,5,pattern);
48178         else draw_arrow(_width - 1,y,0,y,color,opacity,30,5,pattern);
48179         cimg_foroff(values_x,x) {
48180           cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)values_x(x),round_x):(double)values_x(x));
48181           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
48182           const int
48183             xi = (int)(x*(_width - 1)/siz),
48184             _xt = xi - a_label.width()/2,
48185             xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
48186           draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity);
48187           if (allow_zero || *txt!='0' || txt[1]!=0)
48188             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
48189         }
48190       }
48191       return *this;
48192     }
48193 
48194     //! Draw a labeled vertical axis.
48195     /**
48196        \param x X-coordinate of the vertical axis in the image instance.
48197        \param values_y Values along the Y-axis.
48198        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48199        \param opacity Drawing opacity.
48200        \param pattern Drawing pattern.
48201        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
48202        \param allow_zero Enable/disable the drawing of label '0' if found.
48203     **/
48204     template<typename t, typename tc>
48205     CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
48206                        const tc *const color, const float opacity=1,
48207                        const unsigned int pattern=~0U, const unsigned int font_height=13,
48208                        const bool allow_zero=true, const float round_y=0) {
48209       if (is_empty()) return *this;
48210       int siz = (int)values_y.size() - 1;
48211       CImg<charT> txt(32);
48212       CImg<T> a_label;
48213       if (siz<=0) { // Degenerated case
48214         draw_line(x,0,x,_height - 1,color,opacity,pattern);
48215         if (!siz) {
48216           cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)*values_y,round_y):(double)*values_y);
48217           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
48218           const int
48219             _yt = (height() - a_label.height())/2,
48220             yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
48221             _xt = x - 2 - a_label.width(),
48222             xt = _xt>=0?_xt:x + 3;
48223           draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity);
48224           if (allow_zero || *txt!='0' || txt[1]!=0)
48225             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
48226         }
48227       } else { // Regular case
48228         if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height - 1,color,opacity,30,5,pattern);
48229         else draw_arrow(x,_height - 1,x,0,color,opacity,30,5,pattern);
48230         cimg_foroff(values_y,y) {
48231           cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)values_y(y),round_y):(double)values_y(y));
48232           a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
48233           const int
48234             yi = (int)(y*(_height - 1)/siz),
48235             _yt = yi - a_label.height()/2,
48236             yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
48237             _xt = x - 2 - a_label.width(),
48238             xt = _xt>=0?_xt:x + 3;
48239           draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity);
48240           if (allow_zero || *txt!='0' || txt[1]!=0)
48241             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
48242         }
48243       }
48244       return *this;
48245     }
48246 
48247     //! Draw labeled horizontal and vertical axes.
48248     /**
48249        \param values_x Values along the X-axis.
48250        \param values_y Values along the Y-axis.
48251        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48252        \param opacity Drawing opacity.
48253        \param pattern_x Drawing pattern for the X-axis.
48254        \param pattern_y Drawing pattern for the Y-axis.
48255        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
48256        \param allow_zero Enable/disable the drawing of label '0' if found.
48257     **/
48258     template<typename tx, typename ty, typename tc>
48259     CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
48260                        const tc *const color, const float opacity=1,
48261                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
48262                        const unsigned int font_height=13, const bool allow_zero=true,
48263                        const float round_x=0, const float round_y=0) {
48264       if (is_empty()) return *this;
48265       const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
48266       const int sizx = (int)values_x.size() - 1, wm1 = width() - 1;
48267       if (sizx>=0) {
48268         float ox = (float)*nvalues_x;
48269         for (unsigned int x = sizx?1U:0U; x<_width; ++x) {
48270           const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
48271           if (nx*ox<=0) {
48272             draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero,round_y);
48273             break;
48274           }
48275           ox = nx;
48276         }
48277       }
48278       const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
48279       const int sizy = (int)values_y.size() - 1, hm1 = height() - 1;
48280       if (sizy>0) {
48281         float oy = (float)nvalues_y[0];
48282         for (unsigned int y = sizy?1U:0U; y<_height; ++y) {
48283           const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
48284           if (ny*oy<=0) {
48285             draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero,round_x);
48286             break;
48287           }
48288           oy = ny;
48289         }
48290       }
48291       return *this;
48292     }
48293 
48294     //! Draw labeled horizontal and vertical axes \overloading.
48295     template<typename tc>
48296     CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
48297                        const tc *const color, const float opacity=1,
48298                        const int subdivisionx=-60, const int subdivisiony=-60,
48299                        const float precisionx=0, const float precisiony=0,
48300                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
48301                        const unsigned int font_height=13) {
48302       if (is_empty()) return *this;
48303       const bool allow_zero = (x0*x1>0) || (y0*y1>0);
48304       const float
48305         dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0),
48306         px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx,
48307         py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony;
48308       if (x0!=x1 && y0!=y1)
48309         draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),
48310                   CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
48311                   color,opacity,pattern_x,pattern_y,font_height,allow_zero,px,py);
48312       else if (x0==x1 && y0!=y1)
48313         draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
48314                   color,opacity,pattern_y,font_height,py);
48315       else if (x0!=x1 && y0==y1)
48316         draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),(int)y0,
48317                   color,opacity,pattern_x,font_height,px);
48318       return *this;
48319     }
48320 
48321     //! Draw 2D grid.
48322     /**
48323        \param values_x X-coordinates of the vertical lines.
48324        \param values_y Y-coordinates of the horizontal lines.
48325        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48326        \param opacity Drawing opacity.
48327        \param pattern_x Drawing pattern for vertical lines.
48328        \param pattern_y Drawing pattern for horizontal lines.
48329     **/
48330     template<typename tx, typename ty, typename tc>
48331     CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
48332                        const tc *const color, const float opacity=1,
48333                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
48334       if (is_empty()) return *this;
48335       if (values_x) cimg_foroff(values_x,x) {
48336           const int xi = (int)values_x[x];
48337           if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height - 1,color,opacity,pattern_x);
48338         }
48339       if (values_y) cimg_foroff(values_y,y) {
48340           const int yi = (int)values_y[y];
48341           if (yi>=0 && yi<height()) draw_line(0,yi,_width - 1,yi,color,opacity,pattern_y);
48342         }
48343       return *this;
48344     }
48345 
48346     //! Draw 2D grid \simplification.
48347     template<typename tc>
48348     CImg<T>& draw_grid(const float delta_x,  const float delta_y,
48349                        const float offsetx, const float offsety,
48350                        const bool invertx, const bool inverty,
48351                        const tc *const color, const float opacity=1,
48352                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
48353       if (is_empty()) return *this;
48354       CImg<uintT> seqx, seqy;
48355       if (delta_x!=0) {
48356         const float dx = delta_x>0?delta_x:_width*-delta_x/100;
48357         const unsigned int nx = (unsigned int)(_width/dx);
48358         seqx = CImg<uintT>::sequence(1 + nx,0,(unsigned int)(dx*nx));
48359         if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width);
48360         if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
48361       }
48362       if (delta_y!=0) {
48363         const float dy = delta_y>0?delta_y:_height*-delta_y/100;
48364         const unsigned int ny = (unsigned int)(_height/dy);
48365         seqy = CImg<uintT>::sequence(1 + ny,0,(unsigned int)(dy*ny));
48366         if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height);
48367         if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
48368      }
48369       return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
48370     }
48371 
48372     //! Draw 1D graph.
48373     /**
48374        \param data Image containing the graph values I = f(x).
48375        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48376        \param opacity Drawing opacity.
48377 
48378        \param plot_type Define the type of the plot:
48379                       - 0 = No plot.
48380                       - 1 = Plot using segments.
48381                       - 2 = Plot using cubic splines.
48382                       - 3 = Plot with bars.
48383        \param vertex_type Define the type of points:
48384                       - 0 = No points.
48385                       - 1 = Point.
48386                       - 2 = Straight cross.
48387                       - 3 = Diagonal cross.
48388                       - 4 = Filled circle.
48389                       - 5 = Outlined circle.
48390                       - 6 = Square.
48391                       - 7 = Diamond.
48392        \param ymin Lower bound of the y-range.
48393        \param ymax Upper bound of the y-range.
48394        \param pattern Drawing pattern.
48395        \note
48396          - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
48397     **/
48398     template<typename t, typename tc>
48399     CImg<T>& draw_graph(const CImg<t>& data,
48400                         const tc *const color, const float opacity=1,
48401                         const unsigned int plot_type=1, const int vertex_type=1,
48402                         const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
48403       if (is_empty() || _height<=1) return *this;
48404       if (!color)
48405         throw CImgArgumentException(_cimg_instance
48406                                     "draw_graph(): Specified color is (null).",
48407                                     cimg_instance);
48408 
48409       // Create shaded colors for displaying bar plots.
48410       CImg<tc> color1, color2;
48411       if (plot_type==3) {
48412         color1.assign(_spectrum); color2.assign(_spectrum);
48413         cimg_forC(*this,c) {
48414           color1[c] = (tc)std::min((float)cimg::type<tc>::max(),(float)color[c]*1.2f);
48415           color2[c] = (tc)(color[c]*0.4f);
48416         }
48417       }
48418 
48419       // Compute min/max and normalization factors.
48420       const ulongT
48421         siz = data.size(),
48422         _siz1 = siz - (plot_type!=3),
48423         siz1 = _siz1?_siz1:1;
48424       const unsigned int
48425         _width1 = _width - (plot_type!=3),
48426         width1 = _width1?_width1:1;
48427       double m = ymin, M = ymax;
48428       if (ymin==ymax) m = (double)data.max_min(M);
48429       if (m==M) { --m; ++M; }
48430       const float ca = (float)(M-m)/(_height - 1);
48431       bool init_hatch = true;
48432 
48433       // Draw graph edges
48434       switch (plot_type%4) {
48435       case 1 : { // Segments
48436         int oX = 0, oY = (int)cimg::round((data[0] - m)/ca);
48437         if (siz==1) {
48438           const int Y = (int)cimg::round((*data - m)/ca);
48439           draw_line(0,Y,width() - 1,Y,color,opacity,pattern);
48440         } else {
48441           const float fx = (float)_width/siz1;
48442           for (ulongT off = 1; off<siz; ++off) {
48443             const int
48444               X = (int)cimg::round(off*fx) - 1,
48445               Y = (int)cimg::round((data[off]-m)/ca);
48446             draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
48447             oX = X; oY = Y;
48448             init_hatch = false;
48449           }
48450         }
48451       } break;
48452       case 2 : { // Spline
48453         const CImg<t> ndata(data._data,siz,1,1,1,true);
48454         int oY = (int)cimg::round((data[0] - m)/ca);
48455         cimg_forX(*this,x) {
48456           const int Y = (int)cimg::round((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
48457           if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch);
48458           init_hatch = false;
48459           oY = Y;
48460         }
48461       } break;
48462       case 3 : { // Bars
48463         const int Y0 = (int)cimg::round(-m/ca);
48464         const float fx = (float)_width/siz1;
48465         int oX = 0;
48466         cimg_foroff(data,off) {
48467           const int
48468             X = (int)cimg::round((off + 1)*fx) - 1,
48469             Y = (int)cimg::round((data[off] - m)/ca);
48470           draw_rectangle(oX,Y0,X,Y,color,opacity).
48471             draw_line(oX,Y,oX,Y0,color2.data(),opacity).
48472             draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
48473             draw_line(X,Y,X,Y0,color1.data(),opacity).
48474             draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
48475           oX = X + 1;
48476         }
48477       } break;
48478       default : break; // No edges
48479       }
48480 
48481       // Draw graph points
48482       const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
48483       const float fx = (float)_width1/siz1;
48484       switch (vertex_type%8) {
48485       case 1 : { // Point
48486         cimg_foroff(data,off) {
48487           const int
48488             X = (int)cimg::round(off*fx + wb2),
48489             Y = (int)cimg::round((data[off]-m)/ca);
48490           draw_point(X,Y,color,opacity);
48491         }
48492       } break;
48493       case 2 : { // Straight Cross
48494         cimg_foroff(data,off) {
48495           const int
48496             X = (int)cimg::round(off*fx + wb2),
48497             Y = (int)cimg::round((data[off]-m)/ca);
48498           draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity);
48499         }
48500       } break;
48501       case 3 : { // Diagonal Cross
48502         cimg_foroff(data,off) {
48503           const int
48504             X = (int)cimg::round(off*fx + wb2),
48505             Y = (int)cimg::round((data[off]-m)/ca);
48506           draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity);
48507         }
48508       } break;
48509       case 4 : { // Filled Circle
48510         cimg_foroff(data,off) {
48511           const int
48512             X = (int)cimg::round(off*fx + wb2),
48513             Y = (int)cimg::round((data[off]-m)/ca);
48514           draw_circle(X,Y,3,color,opacity);
48515         }
48516       } break;
48517       case 5 : { // Outlined circle
48518         cimg_foroff(data,off) {
48519           const int
48520             X = (int)cimg::round(off*fx + wb2),
48521             Y = (int)cimg::round((data[off]-m)/ca);
48522           draw_circle(X,Y,3,color,opacity,~0U);
48523         }
48524       } break;
48525       case 6 : { // Square
48526         cimg_foroff(data,off) {
48527           const int
48528             X = (int)cimg::round(off*fx + wb2),
48529             Y = (int)cimg::round((data[off]-m)/ca);
48530           draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U);
48531         }
48532       } break;
48533       case 7 : { // Diamond
48534         cimg_foroff(data,off) {
48535           const int
48536             X = (int)cimg::round(off*fx + wb2),
48537             Y = (int)cimg::round((data[off]-m)/ca);
48538           draw_line(X,Y - 4,X + 4,Y,color,opacity).
48539             draw_line(X + 4,Y,X,Y + 4,color,opacity).
48540             draw_line(X,Y + 4,X - 4,Y,color,opacity).
48541             draw_line(X - 4,Y,X,Y - 4,color,opacity);
48542         }
48543       } break;
48544       default : break; // No points
48545       }
48546       return *this;
48547     }
48548 
48549     bool _draw_fill(const int x, const int y, const int z,
48550                     const CImg<T>& ref, const float tolerance2) const {
48551       const T *ptr1 = data(x,y,z), *ptr2 = ref._data;
48552       const unsigned long off = _width*_height*_depth;
48553       float diff = 0;
48554       cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; }
48555       return diff<=tolerance2;
48556     }
48557 
48558     //! Draw filled 3D region with the flood fill algorithm.
48559     /**
48560        \param x0 X-coordinate of the starting point of the region to fill.
48561        \param y0 Y-coordinate of the starting point of the region to fill.
48562        \param z0 Z-coordinate of the starting point of the region to fill.
48563        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48564        \param[out] region Image that will contain the mask of the filled region mask, as an output.
48565        \param tolerance Tolerance concerning neighborhood values.
48566        \param opacity Opacity of the drawing.
48567        \param is_high_connectivity Tells if 8-connexity must be used.
48568        \return \c region is initialized with the binary mask of the filled region.
48569     **/
48570     template<typename tc, typename t>
48571     CImg<T>& draw_fill(const int x0, const int y0, const int z0,
48572                         const tc *const color, const float opacity,
48573                         CImg<t> &region,
48574                         const float tolerance = 0,
48575                         const bool is_high_connectivity = false) {
48576 #define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \
48577                                stack[N] = x; stack(N,1) = y; stack(N++,2) = z
48578 #define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2)
48579 #define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2)
48580 
48581       if (!containsXYZC(x0,y0,z0,0)) return *this;
48582       const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f);
48583       const float tolerance2 = cimg::sqr(tolerance);
48584       const CImg<T> ref = get_vector_at(x0,y0,z0);
48585       CImg<uintT> stack(256,1,1,3);
48586       CImg<ucharT> _region(_width,_height,_depth,1,0);
48587       unsigned int N = 0;
48588       int x, y, z;
48589 
48590       _draw_fill_push(x0,y0,z0);
48591       while (N>0) {
48592         _draw_fill_pop(x,y,z);
48593         if (!_region(x,y,z)) {
48594           const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1;
48595           int xl = x, xr = x;
48596 
48597           // Using these booleans reduces the number of pushes drastically.
48598           bool is_yp = false, is_yn = false, is_zp = false, is_zn = false;
48599           for (int step = -1; step<2; step+=2) {
48600             while (x>=0 && x<width() && _draw_fill_is_inside(x,y,z)) {
48601               if (yp>=0 && _draw_fill_is_inside(x,yp,z)) {
48602                 if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; }
48603               } else is_yp = false;
48604               if (yn<height() && _draw_fill_is_inside(x,yn,z)) {
48605                 if (!is_yn) { _draw_fill_push(x,yn,z); is_yn = true; }
48606               } else is_yn = false;
48607               if (depth()>1) {
48608                 if (zp>=0 && _draw_fill_is_inside(x,y,zp)) {
48609                   if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; }
48610                 } else is_zp = false;
48611                 if (zn<depth() && _draw_fill_is_inside(x,y,zn)) {
48612                   if (!is_zn) { _draw_fill_push(x,y,zn); is_zn = true; }
48613                 } else is_zn = false;
48614               }
48615               if (is_high_connectivity) {
48616                 const int xp = x - 1, xn = x + 1;
48617                 if (yp>=0 && !is_yp) {
48618                   if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) {
48619                     _draw_fill_push(xp,yp,z); if (step<0) is_yp = true;
48620                   }
48621                   if (xn<width() && _draw_fill_is_inside(xn,yp,z)) {
48622                     _draw_fill_push(xn,yp,z); if (step>0) is_yp = true;
48623                   }
48624                 }
48625                 if (yn<height() && !is_yn) {
48626                   if (xp>=0 && _draw_fill_is_inside(xp,yn,z)) {
48627                     _draw_fill_push(xp,yn,z); if (step<0) is_yn = true;
48628                   }
48629                   if (xn<width() && _draw_fill_is_inside(xn,yn,z)) {
48630                     _draw_fill_push(xn,yn,z); if (step>0) is_yn = true;
48631                   }
48632                 }
48633                 if (depth()>1) {
48634                   if (zp>=0 && !is_zp) {
48635                     if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) {
48636                       _draw_fill_push(xp,y,zp); if (step<0) is_zp = true;
48637                     }
48638                     if (xn<width() && _draw_fill_is_inside(xn,y,zp)) {
48639                       _draw_fill_push(xn,y,zp); if (step>0) is_zp = true;
48640                     }
48641 
48642                     if (yp>=0 && !is_yp) {
48643                       if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); }
48644                       if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); }
48645                       if (xn<width() && _draw_fill_is_inside(xn,yp,zp)) { _draw_fill_push(xn,yp,zp); }
48646                     }
48647                     if (yn<height() && !is_yn) {
48648                       if (_draw_fill_is_inside(x,yn,zp)) { _draw_fill_push(x,yn,zp); }
48649                       if (xp>=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); }
48650                       if (xn<width() && _draw_fill_is_inside(xn,yn,zp)) { _draw_fill_push(xn,yn,zp); }
48651                     }
48652                   }
48653 
48654                   if (zn<depth() && !is_zn) {
48655                     if (xp>=0 && _draw_fill_is_inside(xp,y,zn)) {
48656                       _draw_fill_push(xp,y,zn); if (step<0) is_zn = true;
48657                     }
48658                     if (xn<width() && _draw_fill_is_inside(xn,y,zn)) {
48659                       _draw_fill_push(xn,y,zn); if (step>0) is_zn = true;
48660                     }
48661 
48662                     if (yp>=0 && !is_yp) {
48663                       if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); }
48664                       if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); }
48665                       if (xn<width() && _draw_fill_is_inside(xn,yp,zn)) { _draw_fill_push(xn,yp,zn); }
48666                     }
48667                     if (yn<height() && !is_yn) {
48668                       if (_draw_fill_is_inside(x,yn,zn)) { _draw_fill_push(x,yn,zn); }
48669                       if (xp>=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); }
48670                       if (xn<width() && _draw_fill_is_inside(xn,yn,zn)) { _draw_fill_push(xn,yn,zn); }
48671                     }
48672                   }
48673                 }
48674               }
48675               x+=step;
48676             }
48677             if (step<0) { xl = ++x; x = xr + 1; is_yp = is_yn = is_zp = is_zn = false; }
48678             else xr = --x;
48679           }
48680           std::memset(_region.data(xl,y,z),1,xr - xl + 1);
48681           if (opacity==1) {
48682             if (sizeof(T)==1) {
48683               const int dx = xr - xl + 1;
48684               cimg_forC(*this,c) std::memset(data(xl,y,z,c),(int)color[c],dx);
48685             } else cimg_forC(*this,c) {
48686                 const T val = (T)color[c];
48687                 T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) *(ptri++) = val;
48688               }
48689           } else cimg_forC(*this,c) {
48690               const T val = (T)(color[c]*nopacity);
48691               T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) { *ptri = (T)(val + *ptri*copacity); ++ptri; }
48692             }
48693         }
48694       }
48695       _region.move_to(region);
48696       return *this;
48697     }
48698 
48699     //! Draw filled 3D region with the flood fill algorithm \simplification.
48700     template<typename tc>
48701     CImg<T>& draw_fill(const int x0, const int y0, const int z0,
48702                        const tc *const color, const float opacity=1,
48703                        const float tolerance=0, const bool is_high_connexity=false) {
48704       CImg<ucharT> tmp;
48705       return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity);
48706     }
48707 
48708     //! Draw filled 2D region with the flood fill algorithm \simplification.
48709     template<typename tc>
48710     CImg<T>& draw_fill(const int x0, const int y0,
48711                        const tc *const color, const float opacity=1,
48712                        const float tolerance=0, const bool is_high_connexity=false) {
48713       CImg<ucharT> tmp;
48714       return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity);
48715     }
48716 
48717     //! Draw a random plasma texture.
48718     /**
48719        \param alpha Alpha-parameter.
48720        \param beta Beta-parameter.
48721        \param scale Scale-parameter.
48722        \note Use the mid-point algorithm to render.
48723     **/
48724     CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
48725       if (is_empty()) return *this;
48726       const int w = width(), h = height();
48727       const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
48728       cimg_uint64 rng = (cimg::_rand(),cimg::rng());
48729       cimg_forZC(*this,z,c) {
48730         CImg<T> ref = get_shared_slice(z,c);
48731         for (int delta = 1<<std::min(scale,31U); delta>1; delta>>=1) {
48732           const int delta2 = delta>>1;
48733           const float r = alpha*delta + beta;
48734 
48735           // Square step.
48736           for (int y0 = 0; y0<h; y0+=delta)
48737             for (int x0 = 0; x0<w; x0+=delta) {
48738               const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h;
48739               const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) +
48740                                           r*cimg::rand(-1,1,&rng));
48741               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
48742             }
48743 
48744           // Diamond steps.
48745           for (int y = -delta2; y<h; y+=delta)
48746             for (int x0=0; x0<w; x0+=delta) {
48747               const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h,
48748                 xc = (x0 + delta2)%w, yc = (y + delta2)%h;
48749               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
48750                                           r*cimg::rand(-1,1,&rng));
48751               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
48752             }
48753           for (int y0 = 0; y0<h; y0+=delta)
48754             for (int x = -delta2; x<w; x+=delta) {
48755               const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h,
48756                 xc = (x + delta2)%w, yc = (y0 + delta2)%h;
48757               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
48758                                           r*cimg::rand(-1,1,&rng));
48759               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
48760             }
48761           for (int y = -delta2; y<h; y+=delta)
48762             for (int x = -delta2; x<w; x+=delta) {
48763               const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h,
48764                 xc = (x + delta2)%w, yc = (y + delta2)%h;
48765               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
48766                                           r*cimg::rand(-1,1,&rng));
48767                 ref(xc,yc) = (T)(val<m?m:val>M?M:val);
48768             }
48769         }
48770       }
48771       cimg::srand(rng);
48772       return *this;
48773     }
48774 
48775     //! Draw a quadratic Mandelbrot or Julia 2D fractal.
48776     /**
48777        \param x0 X-coordinate of the upper-left pixel.
48778        \param y0 Y-coordinate of the upper-left pixel.
48779        \param x1 X-coordinate of the lower-right pixel.
48780        \param y1 Y-coordinate of the lower-right pixel.
48781        \param colormap Colormap.
48782        \param opacity Drawing opacity.
48783        \param z0r Real part of the upper-left fractal vertex.
48784        \param z0i Imaginary part of the upper-left fractal vertex.
48785        \param z1r Real part of the lower-right fractal vertex.
48786        \param z1i Imaginary part of the lower-right fractal vertex.
48787        \param iteration_max Maximum number of iterations for each estimated point.
48788        \param is_normalized_iteration Tells if iterations are normalized.
48789        \param is_julia_set Tells if the Mandelbrot or Julia set is rendered.
48790        \param param_r Real part of the Julia set parameter.
48791        \param param_i Imaginary part of the Julia set parameter.
48792        \note Fractal rendering is done by the Escape Time Algorithm.
48793     **/
48794     template<typename tc>
48795     CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
48796                              const CImg<tc>& colormap, const float opacity=1,
48797                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
48798                              const unsigned int iteration_max=255,
48799                              const bool is_normalized_iteration=false,
48800                              const bool is_julia_set=false,
48801                              const double param_r=0, const double param_i=0) {
48802       if (is_empty()) return *this;
48803       CImg<tc> palette;
48804       if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
48805       if (palette && palette._spectrum!=_spectrum)
48806         throw CImgArgumentException(_cimg_instance
48807                                     "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have "
48808                                     "incompatible dimensions.",
48809                                     cimg_instance,
48810                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
48811 
48812       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.);
48813       const int
48814         _x0 = cimg::cut(x0,0,width() - 1),
48815         _y0 = cimg::cut(y0,0,height() - 1),
48816         _x1 = cimg::cut(x1,0,width() - 1),
48817         _y1 = cimg::cut(y1,0,height() - 1);
48818 
48819       cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
48820                          cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048))
48821       for (int q = _y0; q<=_y1; ++q)
48822         for (int p = _x0; p<=_x1; ++p) {
48823           unsigned int iteration = 0;
48824           const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
48825           double zr, zi, cr, ci;
48826           if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
48827           else { zr = param_r; zi = param_i; cr = x; ci = y; }
48828           for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
48829             const double temp = zr*zr - zi*zi + cr;
48830             zi = 2*zr*zi + ci;
48831             zr = temp;
48832           }
48833           if (iteration>iteration_max) {
48834             if (palette) {
48835               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
48836               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
48837             } else {
48838               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
48839               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
48840             }
48841           } else if (is_normalized_iteration) {
48842             const float
48843               normz = (float)cimg::abs(zr*zr + zi*zi),
48844               niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
48845             if (palette) {
48846               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
48847               else cimg_forC(*this,c)
48848                      (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
48849             } else {
48850               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
48851               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
48852             }
48853           } else {
48854             if (palette) {
48855               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
48856               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
48857             } else {
48858               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
48859               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
48860             }
48861           }
48862         }
48863       return *this;
48864     }
48865 
48866     //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading.
48867     template<typename tc>
48868     CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
48869                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
48870                              const unsigned int iteration_max=255,
48871                              const bool is_normalized_iteration=false,
48872                              const bool is_julia_set=false,
48873                              const double param_r=0, const double param_i=0) {
48874       return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity,
48875                              z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
48876     }
48877 
48878     //! Draw a 1D gaussian function.
48879     /**
48880        \param xc X-coordinate of the gaussian center.
48881        \param sigma Standard variation of the gaussian distribution.
48882        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48883        \param opacity Drawing opacity.
48884     **/
48885     template<typename tc>
48886     CImg<T>& draw_gaussian(const float xc, const float sigma,
48887                            const tc *const color, const float opacity=1) {
48888       if (is_empty()) return *this;
48889       if (!color)
48890         throw CImgArgumentException(_cimg_instance
48891                                     "draw_gaussian(): Specified color is (null).",
48892                                     cimg_instance);
48893       const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
48894       const ulongT whd = (ulongT)_width*_height*_depth;
48895       const tc *col = color;
48896       cimg_forX(*this,x) {
48897         const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
48898         T *ptrd = data(x,0,0,0);
48899         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
48900         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
48901         col-=_spectrum;
48902       }
48903       return *this;
48904     }
48905 
48906     //! Draw a 2D gaussian function.
48907     /**
48908        \param xc X-coordinate of the gaussian center.
48909        \param yc Y-coordinate of the gaussian center.
48910        \param tensor Covariance matrix (must be 2x2).
48911        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
48912        \param opacity Drawing opacity.
48913     **/
48914     template<typename t, typename tc>
48915     CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
48916                            const tc *const color, const float opacity=1) {
48917       if (is_empty()) return *this;
48918       if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
48919         throw CImgArgumentException(_cimg_instance
48920                                     "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
48921                                     cimg_instance,
48922                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
48923       if (!color)
48924         throw CImgArgumentException(_cimg_instance
48925                                     "draw_gaussian(): Specified color is (null).",
48926                                     cimg_instance);
48927       typedef typename CImg<t>::Tfloat tfloat;
48928       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
48929       const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
48930       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
48931       const ulongT whd = (ulongT)_width*_height*_depth;
48932       const tc *col = color;
48933       float dy = -yc;
48934       cimg_forY(*this,y) {
48935         float dx = -xc;
48936         cimg_forX(*this,x) {
48937           const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
48938           T *ptrd = data(x,y,0,0);
48939           if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
48940           else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
48941           col-=_spectrum;
48942           ++dx;
48943         }
48944         ++dy;
48945       }
48946       return *this;
48947     }
48948 
48949     //! Draw a 2D gaussian function \overloading.
48950     template<typename tc>
48951     CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
48952                            const tc *const color, const float opacity=1) {
48953       const double
48954         a = r1*ru*ru + r2*rv*rv,
48955         b = (r1-r2)*ru*rv,
48956         c = r1*rv*rv + r2*ru*ru;
48957       const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
48958       return draw_gaussian(xc,yc,tensor,color,opacity);
48959     }
48960 
48961     //! Draw a 2D gaussian function \overloading.
48962     template<typename tc>
48963     CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
48964                            const tc *const color, const float opacity=1) {
48965       return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
48966     }
48967 
48968     //! Draw a 3D gaussian function \overloading.
48969     template<typename t, typename tc>
48970     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
48971                            const tc *const color, const float opacity=1) {
48972       if (is_empty()) return *this;
48973       typedef typename CImg<t>::Tfloat tfloat;
48974       if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
48975         throw CImgArgumentException(_cimg_instance
48976                                     "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
48977                                     cimg_instance,
48978                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
48979 
48980       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
48981       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);
48982       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
48983       const ulongT whd = (ulongT)_width*_height*_depth;
48984       const tc *col = color;
48985       cimg_forXYZ(*this,x,y,z) {
48986         const float
48987           dx = (x - xc), dy = (y - yc), dz = (z - zc),
48988           val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
48989         T *ptrd = data(x,y,z,0);
48990         if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
48991         else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
48992         col-=_spectrum;
48993       }
48994       return *this;
48995     }
48996 
48997     //! Draw a 3D gaussian function \overloading.
48998     template<typename tc>
48999     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
49000                            const tc *const color, const float opacity=1) {
49001       return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
49002     }
49003 
49004     //! Draw a 3D object.
49005     /**
49006        \param x0 X-coordinate of the 3D object position
49007        \param y0 Y-coordinate of the 3D object position
49008        \param z0 Z-coordinate of the 3D object position
49009        \param vertices Image Nx3 describing 3D point coordinates
49010        \param primitives List of P primitives
49011        \param colors List of P color (or textures)
49012        \param opacities Image or list of P opacities
49013        \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
49014        \param is_double_sided Tells if object faces have two sides or are oriented.
49015        \param focale length of the focale (0 for parallel projection)
49016        \param lightx X-coordinate of the light
49017        \param lighty Y-coordinate of the light
49018        \param lightz Z-coordinate of the light
49019        \param specular_lightness Amount of specular light.
49020        \param specular_shininess Shininess of the object
49021        \param g_opacity Global opacity of the object.
49022     **/
49023     template<typename tp, typename tf, typename tc, typename to>
49024     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49025                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49026                            const CImgList<tc>& colors, const CImg<to>& opacities,
49027                            const unsigned int render_type=4,
49028                            const bool is_double_sided=false, const float focale=700,
49029                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49030                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49031                            const float g_opacity=1) {
49032       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
49033                            is_double_sided,focale,lightx,lighty,lightz,
49034                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49035     }
49036 
49037     //! Draw a 3D object \simplification.
49038     template<typename tp, typename tf, typename tc, typename to, typename tz>
49039     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49040                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49041                            const CImgList<tc>& colors, const CImg<to>& opacities,
49042                            const unsigned int render_type,
49043                            const bool is_double_sided, const float focale,
49044                            const float lightx, const float lighty, const float lightz,
49045                            const float specular_lightness, const float specular_shininess,
49046                            const float g_opacity, CImg<tz>& zbuffer) {
49047       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
49048                             render_type,is_double_sided,focale,lightx,lighty,lightz,
49049                             specular_lightness,specular_shininess,g_opacity,1);
49050     }
49051 
49052 #ifdef cimg_use_board
49053     template<typename tp, typename tf, typename tc, typename to>
49054     CImg<T>& draw_object3d(LibBoard::Board& board,
49055                            const float x0, const float y0, const float z0,
49056                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49057                            const CImgList<tc>& colors, const CImg<to>& opacities,
49058                            const unsigned int render_type=4,
49059                            const bool is_double_sided=false, const float focale=700,
49060                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49061                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49062                            const float g_opacity=1) {
49063       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
49064                            is_double_sided,focale,lightx,lighty,lightz,
49065                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49066     }
49067 
49068     template<typename tp, typename tf, typename tc, typename to, typename tz>
49069     CImg<T>& draw_object3d(LibBoard::Board& board,
49070                            const float x0, const float y0, const float z0,
49071                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49072                            const CImgList<tc>& colors, const CImg<to>& opacities,
49073                            const unsigned int render_type,
49074                            const bool is_double_sided, const float focale,
49075                            const float lightx, const float lighty, const float lightz,
49076                            const float specular_lightness, const float specular_shininess,
49077                            const float g_opacity, CImg<tz>& zbuffer) {
49078       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
49079                             render_type,is_double_sided,focale,lightx,lighty,lightz,
49080                             specular_lightness,specular_shininess,g_opacity,1);
49081     }
49082 #endif
49083 
49084     //! Draw a 3D object \simplification.
49085     template<typename tp, typename tf, typename tc, typename to>
49086     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49087                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49088                            const CImgList<tc>& colors, const CImgList<to>& opacities,
49089                            const unsigned int render_type=4,
49090                            const bool is_double_sided=false, const float focale=700,
49091                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49092                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49093                            const float g_opacity=1) {
49094       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
49095                            is_double_sided,focale,lightx,lighty,lightz,
49096                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49097     }
49098 
49099     //! Draw a 3D object \simplification.
49100     template<typename tp, typename tf, typename tc, typename to, typename tz>
49101     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49102                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49103                            const CImgList<tc>& colors, const CImgList<to>& opacities,
49104                            const unsigned int render_type,
49105                            const bool is_double_sided, const float focale,
49106                            const float lightx, const float lighty, const float lightz,
49107                            const float specular_lightness, const float specular_shininess,
49108                            const float g_opacity, CImg<tz>& zbuffer) {
49109       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
49110                             render_type,is_double_sided,focale,lightx,lighty,lightz,
49111                             specular_lightness,specular_shininess,g_opacity,1);
49112     }
49113 
49114 #ifdef cimg_use_board
49115     template<typename tp, typename tf, typename tc, typename to>
49116     CImg<T>& draw_object3d(LibBoard::Board& board,
49117                            const float x0, const float y0, const float z0,
49118                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49119                            const CImgList<tc>& colors, const CImgList<to>& opacities,
49120                            const unsigned int render_type=4,
49121                            const bool is_double_sided=false, const float focale=700,
49122                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49123                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49124                            const float g_opacity=1) {
49125       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
49126                            is_double_sided,focale,lightx,lighty,lightz,
49127                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49128     }
49129 
49130     template<typename tp, typename tf, typename tc, typename to, typename tz>
49131     CImg<T>& draw_object3d(LibBoard::Board& board,
49132                            const float x0, const float y0, const float z0,
49133                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49134                            const CImgList<tc>& colors, const CImgList<to>& opacities,
49135                            const unsigned int render_type,
49136                            const bool is_double_sided, const float focale,
49137                            const float lightx, const float lighty, const float lightz,
49138                            const float specular_lightness, const float specular_shininess,
49139                            const float g_opacity, CImg<tz>& zbuffer) {
49140       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
49141                             render_type,is_double_sided,focale,lightx,lighty,lightz,
49142                             specular_lightness,specular_shininess,g_opacity,1);
49143     }
49144 #endif
49145 
49146     //! Draw a 3D object \simplification.
49147     template<typename tp, typename tf, typename tc>
49148     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49149                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49150                            const CImgList<tc>& colors,
49151                            const unsigned int render_type=4,
49152                            const bool is_double_sided=false, const float focale=700,
49153                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49154                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49155                            const float g_opacity=1) {
49156       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
49157                            render_type,is_double_sided,focale,lightx,lighty,lightz,
49158                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49159     }
49160 
49161     //! Draw a 3D object \simplification.
49162     template<typename tp, typename tf, typename tc, typename tz>
49163     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
49164                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49165                            const CImgList<tc>& colors,
49166                            const unsigned int render_type,
49167                            const bool is_double_sided, const float focale,
49168                            const float lightx, const float lighty, const float lightz,
49169                            const float specular_lightness, const float specular_shininess,
49170                            const float g_opacity, CImg<tz>& zbuffer) {
49171       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
49172                            render_type,is_double_sided,focale,lightx,lighty,lightz,
49173                            specular_lightness,specular_shininess,g_opacity,zbuffer);
49174     }
49175 
49176 #ifdef cimg_use_board
49177     template<typename tp, typename tf, typename tc, typename to>
49178     CImg<T>& draw_object3d(LibBoard::Board& board,
49179                            const float x0, const float y0, const float z0,
49180                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49181                            const CImgList<tc>& colors,
49182                            const unsigned int render_type=4,
49183                            const bool is_double_sided=false, const float focale=700,
49184                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
49185                            const float specular_lightness=0.2f, const float specular_shininess=0.1f,
49186                            const float g_opacity=1) {
49187       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
49188                            render_type,is_double_sided,focale,lightx,lighty,lightz,
49189                            specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
49190     }
49191 
49192     template<typename tp, typename tf, typename tc, typename to, typename tz>
49193     CImg<T>& draw_object3d(LibBoard::Board& board,
49194                            const float x0, const float y0, const float z0,
49195                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
49196                            const CImgList<tc>& colors,
49197                            const unsigned int render_type,
49198                            const bool is_double_sided, const float focale,
49199                            const float lightx, const float lighty, const float lightz,
49200                            const float specular_lightness, const float specular_shininess,
49201                            const float g_opacity, CImg<tz>& zbuffer) {
49202       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
49203                            render_type,is_double_sided,focale,lightx,lighty,lightz,
49204                            specular_lightness,specular_shininess,g_opacity,zbuffer);
49205     }
49206 #endif
49207 
49208     template<typename t, typename to>
49209     static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
49210       if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; }
49211       if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); }
49212       opacity.assign(opacities[n_primitive],true);
49213       return 1.f;
49214     }
49215 
49216     template<typename t, typename to>
49217     static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
49218       opacity.assign();
49219       return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive];
49220     }
49221 
49222     template<typename t>
49223     static float ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) {
49224       return n_primitive<opacities._width && opacities[n_primitive].size()==1?(float)opacities(n_primitive,0):1.f;
49225     }
49226 
49227     template<typename t>
49228     static float ___draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive) {
49229       return n_primitive<opacities._width?(float)opacities[n_primitive]:1.f;
49230     }
49231 
49232     template<typename tz, typename tp, typename tf, typename tc, typename to>
49233     CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
49234                             const float X, const float Y, const float Z,
49235                             const CImg<tp>& vertices,
49236                             const CImgList<tf>& primitives,
49237                             const CImgList<tc>& colors,
49238                             const to& opacities,
49239                             const unsigned int render_type,
49240                             const bool is_double_sided, const float focale,
49241                             const float lightx, const float lighty, const float lightz,
49242                             const float specular_lightness, const float specular_shininess,
49243                             const float g_opacity, const float sprite_scale) {
49244       typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
49245       typedef typename to::value_type _to;
49246       if (is_empty() || !vertices || !primitives) return *this;
49247       CImg<char> error_message(1024);
49248       if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
49249         throw CImgArgumentException(_cimg_instance
49250                                     "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).",
49251                                     cimg_instance,vertices._width,primitives._width,error_message.data());
49252 #ifndef cimg_use_board
49253       if (pboard) return *this;
49254 #endif
49255       if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety
49256 
49257       const float
49258         nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)),
49259         nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess),
49260         nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
49261         nsl2 = 1 - 2*nsl1*nspec,
49262         nsl3 = nspec2 - nsl1 - nsl2;
49263 
49264       // Create light texture for phong-like rendering.
49265       CImg<floatT> light_texture;
49266       if (render_type==5) {
49267         if (colors._width>primitives._width) {
49268           static CImg<floatT> default_light_texture;
49269           static const tc *lptr = 0;
49270           static tc ref_values[64] = { 0 };
49271           const CImg<tc>& img = colors.back();
49272           bool is_same_texture = (lptr==img._data);
49273           if (is_same_texture)
49274             for (unsigned int r = 0, j = 0; j<8; ++j)
49275               for (unsigned int i = 0; i<8; ++i)
49276                 if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) {
49277                   is_same_texture = false; break;
49278                 }
49279           if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
49280             (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
49281             lptr = colors.back().data();
49282             for (unsigned int r = 0, j = 0; j<8; ++j)
49283               for (unsigned int i = 0; i<8; ++i)
49284                 ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum);
49285           }
49286           light_texture.assign(default_light_texture,true);
49287         } else {
49288           static CImg<floatT> default_light_texture;
49289           static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
49290           if (!default_light_texture ||
49291               lightx!=olightx || lighty!=olighty || lightz!=olightz ||
49292               specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
49293             default_light_texture.assign(512,512);
49294             const float
49295               dlx = lightx - X,
49296               dly = lighty - Y,
49297               dlz = lightz - Z,
49298               nl = cimg::hypot(dlx,dly,dlz),
49299               nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl),
49300               nly = (default_light_texture._height - 1)/2*(1 + dly/nl),
49301               white[] = { 1 };
49302             default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white);
49303             cimg_forXY(default_light_texture,x,y) {
49304               const float factor = default_light_texture(x,y);
49305               if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3);
49306             }
49307             default_light_texture.resize(-100,-100,1,_spectrum);
49308             olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
49309           }
49310           light_texture.assign(default_light_texture,true);
49311         }
49312       }
49313 
49314       // Compute 3D to 2D projection.
49315       CImg<tpfloat> projections(vertices._width,2);
49316       tpfloat parallzmin = cimg::type<tpfloat>::max();
49317       const float absfocale = focale?cimg::abs(focale):0;
49318       if (absfocale) {
49319         cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
49320         cimg_forX(projections,l) { // Perspective projection
49321           const tpfloat
49322             x = (tpfloat)vertices(l,0),
49323             y = (tpfloat)vertices(l,1),
49324             z = (tpfloat)vertices(l,2);
49325           const tpfloat projectedz = z + Z + absfocale;
49326           projections(l,1) = Y + absfocale*y/projectedz;
49327           projections(l,0) = X + absfocale*x/projectedz;
49328         }
49329       } else {
49330         cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
49331         cimg_forX(projections,l) { // Parallel projection
49332           const tpfloat
49333             x = (tpfloat)vertices(l,0),
49334             y = (tpfloat)vertices(l,1),
49335             z = (tpfloat)vertices(l,2);
49336           if (z<parallzmin) parallzmin = z;
49337           projections(l,1) = Y + y;
49338           projections(l,0) = X + x;
49339         }
49340       }
49341 
49342       const float _focale = absfocale?absfocale:(1e5f-parallzmin);
49343       float zmax = 0;
49344       if (zbuffer) zmax = vertices.get_shared_row(2).max();
49345 
49346       // Compute visible primitives.
49347       CImg<uintT> visibles(primitives._width,1,1,1,~0U);
49348       CImg<tpfloat> zrange(primitives._width);
49349       const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
49350       bool is_forward = zbuffer?true:false;
49351 
49352       cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096))
49353       cimglist_for(primitives,l) {
49354         const CImg<tf>& primitive = primitives[l];
49355         switch (primitive.size()) {
49356         case 1 : { // Point
49357           CImg<_to> _opacity;
49358           __draw_object3d(opacities,l,_opacity);
49359           if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false;
49360           const unsigned int i0 = (unsigned int)primitive(0);
49361           const tpfloat z0 = Z + vertices(i0,2);
49362           if (z0>zmin) {
49363             visibles(l) = (unsigned int)l;
49364             zrange(l) = z0;
49365           }
49366         } break;
49367         case 5 : { // Sphere
49368           const unsigned int
49369             i0 = (unsigned int)primitive(0),
49370             i1 = (unsigned int)primitive(1);
49371           const tpfloat
49372             Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
49373             Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
49374             Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
49375             _zc = Z + Zc,
49376             zc = _zc + _focale,
49377             xc = X + Xc*(absfocale?absfocale/zc:1),
49378             yc = Y + Yc*(absfocale?absfocale/zc:1),
49379             radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0),
49380                                       vertices(i1,1) - vertices(i0,1),
49381                                       vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1),
49382             xm = xc - radius,
49383             ym = yc - radius,
49384             xM = xc + radius,
49385             yM = yc + radius;
49386           if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
49387             visibles(l) = (unsigned int)l;
49388             zrange(l) = _zc;
49389           }
49390           is_forward = false;
49391         } break;
49392         case 2 : case 6 : { // Segment
49393           const unsigned int
49394             i0 = (unsigned int)primitive(0),
49395             i1 = (unsigned int)primitive(1);
49396           const tpfloat
49397             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
49398             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
49399           tpfloat xm, xM, ym, yM;
49400           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
49401           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
49402           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
49403             visibles(l) = (unsigned int)l;
49404             zrange(l) = (z0 + z1)/2;
49405           }
49406         } break;
49407         case 3 : case 9 : { // Triangle
49408           const unsigned int
49409             i0 = (unsigned int)primitive(0),
49410             i1 = (unsigned int)primitive(1),
49411             i2 = (unsigned int)primitive(2);
49412           const tpfloat
49413             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
49414             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
49415             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
49416           tpfloat xm, xM, ym, yM;
49417           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
49418           if (x2<xm) xm = x2;
49419           if (x2>xM) xM = x2;
49420           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
49421           if (y2<ym) ym = y2;
49422           if (y2>yM) yM = y2;
49423           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
49424             const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
49425             if (is_double_sided || d<0) {
49426               visibles(l) = (unsigned int)l;
49427               zrange(l) = (z0 + z1 + z2)/3;
49428             }
49429           }
49430         } break;
49431         case 4 : case 12 : { // Quadrangle
49432           const unsigned int
49433             i0 = (unsigned int)primitive(0),
49434             i1 = (unsigned int)primitive(1),
49435             i2 = (unsigned int)primitive(2),
49436             i3 = (unsigned int)primitive(3);
49437           const tpfloat
49438             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
49439             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
49440             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
49441             x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
49442           tpfloat xm, xM, ym, yM;
49443           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
49444           if (x2<xm) xm = x2;
49445           if (x2>xM) xM = x2;
49446           if (x3<xm) xm = x3;
49447           if (x3>xM) xM = x3;
49448           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
49449           if (y2<ym) ym = y2;
49450           if (y2>yM) yM = y2;
49451           if (y3<ym) ym = y3;
49452           if (y3>yM) yM = y3;
49453           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) {
49454             const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
49455             if (is_double_sided || d<0) {
49456               visibles(l) = (unsigned int)l;
49457               zrange(l) = (z0 + z1 + z2 + z3)/4;
49458             }
49459           }
49460         } break;
49461         default :
49462           if (render_type==5) cimg::mutex(10,0);
49463           throw CImgArgumentException(_cimg_instance
49464                                       "draw_object3d(): Invalid primitive[%u] with size %u "
49465                                       "(should have size 1,2,3,4,5,6,9 or 12).",
49466                                       cimg_instance,
49467                                       l,primitive.size());
49468         }
49469       }
49470 
49471       // Force transparent primitives to be drawn last when zbuffer is activated
49472       // (and if object contains no spheres or sprites).
49473       if (is_forward)
49474         cimglist_for(primitives,l)
49475           if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l);
49476 
49477       // Sort only visibles primitives.
49478       unsigned int *p_visibles = visibles._data;
49479       tpfloat *p_zrange = zrange._data;
49480       const tpfloat *ptrz = p_zrange;
49481       cimg_for(visibles,ptr,unsigned int) {
49482         if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
49483         ++ptrz;
49484       }
49485       const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data);
49486       if (!nb_visibles) {
49487         if (render_type==5) cimg::mutex(10,0);
49488         return *this;
49489       }
49490       CImg<uintT> permutations;
49491       CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward);
49492 
49493       // Compute light properties
49494       CImg<floatT> lightprops;
49495       switch (render_type) {
49496       case 3 : { // Flat Shading
49497         lightprops.assign(nb_visibles);
49498         cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
49499         cimg_forX(lightprops,l) {
49500           const CImg<tf>& primitive = primitives(visibles(permutations(l)));
49501           const unsigned int psize = (unsigned int)primitive.size();
49502           if (psize==3 || psize==4 || psize==9 || psize==12) {
49503             const unsigned int
49504               i0 = (unsigned int)primitive(0),
49505               i1 = (unsigned int)primitive(1),
49506               i2 = (unsigned int)primitive(2);
49507             const tpfloat
49508               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
49509               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
49510               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
49511               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
49512               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
49513               nx = dy1*dz2 - dz1*dy2,
49514               ny = dz1*dx2 - dx1*dz2,
49515               nz = dx1*dy2 - dy1*dx2,
49516               norm = 1e-5f + cimg::hypot(nx,ny,nz),
49517               lx = X + (x0 + x1 + x2)/3 - lightx,
49518               ly = Y + (y0 + y1 + y2)/3 - lighty,
49519               lz = Z + (z0 + z1 + z2)/3 - lightz,
49520               nl = 1e-5f + cimg::hypot(lx,ly,lz),
49521               factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
49522             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
49523           } else lightprops[l] = 1;
49524         }
49525       } break;
49526 
49527       case 4 : // Gouraud Shading
49528       case 5 : { // Phong-Shading
49529         CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0);
49530         cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
49531         for (int l = 0; l<(int)nb_visibles; ++l) {
49532           const CImg<tf>& primitive = primitives[visibles(l)];
49533           const unsigned int psize = (unsigned int)primitive.size();
49534           const bool
49535             triangle_flag = (psize==3) || (psize==9),
49536             quadrangle_flag = (psize==4) || (psize==12);
49537           if (triangle_flag || quadrangle_flag) {
49538             const unsigned int
49539               i0 = (unsigned int)primitive(0),
49540               i1 = (unsigned int)primitive(1),
49541               i2 = (unsigned int)primitive(2),
49542               i3 = quadrangle_flag?(unsigned int)primitive(3):0;
49543             const tpfloat
49544               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
49545               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
49546               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
49547               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
49548               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
49549               nnx = dy1*dz2 - dz1*dy2,
49550               nny = dz1*dx2 - dx1*dz2,
49551               nnz = dx1*dy2 - dy1*dx2,
49552               norm = 1e-5f + cimg::hypot(nnx,nny,nnz),
49553               nx = nnx/norm,
49554               ny = nny/norm,
49555               nz = nnz/norm;
49556             unsigned int ix = 0, iy = 1, iz = 2;
49557             if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; }
49558             vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz;
49559             vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz;
49560             vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz;
49561             if (quadrangle_flag) {
49562               vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz;
49563             }
49564           }
49565         }
49566 
49567         if (is_double_sided) cimg_forX(vertices_normals,p) {
49568             const float
49569               nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2),
49570               nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5),
49571               n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1;
49572             if (n1>n0) {
49573               vertices_normals(p,0) = -nx1;
49574               vertices_normals(p,1) = -ny1;
49575               vertices_normals(p,2) = -nz1;
49576             }
49577           }
49578 
49579         if (render_type==4) {
49580           lightprops.assign(vertices._width);
49581           cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
49582           cimg_forX(lightprops,l) {
49583             const tpfloat
49584               nx = vertices_normals(l,0),
49585               ny = vertices_normals(l,1),
49586               nz = vertices_normals(l,2),
49587               norm = 1e-5f + cimg::hypot(nx,ny,nz),
49588               lx = X + vertices(l,0) - lightx,
49589               ly = Y + vertices(l,1) - lighty,
49590               lz = Z + vertices(l,2) - lightz,
49591               nl = 1e-5f + cimg::hypot(lx,ly,lz),
49592               factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
49593             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
49594           }
49595         } else {
49596           const unsigned int
49597             lw2 = light_texture._width/2 - 1,
49598             lh2 = light_texture._height/2 - 1;
49599           lightprops.assign(vertices._width,2);
49600           cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
49601           cimg_forX(lightprops,l) {
49602             const tpfloat
49603               nx = vertices_normals(l,0),
49604               ny = vertices_normals(l,1),
49605               nz = vertices_normals(l,2),
49606               norm = 1e-5f + cimg::hypot(nx,ny,nz),
49607               nnx = nx/norm,
49608               nny = ny/norm;
49609             lightprops(l,0) = lw2*(1 + nnx);
49610             lightprops(l,1) = lh2*(1 + nny);
49611           }
49612         }
49613       } break;
49614       }
49615 
49616       // Draw visible primitives
49617       const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
49618       CImg<_to> _opacity;
49619 
49620       for (unsigned int l = 0; l<nb_visibles; ++l) {
49621         const unsigned int n_primitive = visibles(permutations(l));
49622         const CImg<tf>& primitive = primitives[n_primitive];
49623         const CImg<tc>
49624           &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
49625           _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
49626             __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
49627           &color = _color?_color:(__color?__color:default_color);
49628         const tc *const pcolor = color._data;
49629         float opacity = __draw_object3d(opacities,n_primitive,_opacity);
49630         if (_opacity.is_empty()) opacity*=g_opacity;
49631 
49632 #ifdef cimg_use_board
49633         LibBoard::Board &board = *(LibBoard::Board*)pboard;
49634 #endif
49635 
49636         switch (primitive.size()) {
49637         case 1 : { // Colored point or sprite
49638           const unsigned int n0 = (unsigned int)primitive[0];
49639           const int x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1));
49640 
49641           if (_opacity.is_empty()) { // Scalar opacity
49642 
49643             if (color.size()==_spectrum) { // Colored point
49644               draw_point(x0,y0,pcolor,opacity);
49645 
49646 #ifdef cimg_use_board
49647               if (pboard) {
49648                 board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49649                 board.drawDot((float)x0,height()-(float)y0);
49650               }
49651 #endif
49652             } else { // Sprite
49653               const tpfloat z = Z + vertices(n0,2);
49654               const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
49655               const unsigned int
49656                 _sw = (unsigned int)(color._width*factor),
49657                 _sh = (unsigned int)(color._height*factor),
49658                 sw = _sw?_sw:1, sh = _sh?_sh:1;
49659               const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
49660               if (sw<=3*_width/2 && sh<=3*_height/2 &&
49661                   (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
49662                 const CImg<tc>
49663                   _sprite = (sw!=color._width || sh!=color._height)?
49664                     color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
49665                   &sprite = _sprite?_sprite:color;
49666                 draw_image(nx0,ny0,sprite,opacity);
49667 
49668 #ifdef cimg_use_board
49669                 if (pboard) {
49670                   board.setPenColorRGBi(128,128,128);
49671                   board.setFillColor(LibBoard::Color::Null);
49672                   board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
49673                 }
49674 #endif
49675               }
49676             }
49677           } else { // Opacity mask
49678             const tpfloat z = Z + vertices(n0,2);
49679             const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
49680             const unsigned int
49681               _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor),
49682               _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor),
49683               sw = _sw?_sw:1, sh = _sh?_sh:1;
49684             const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
49685             if (sw<=3*_width/2 && sh<=3*_height/2 &&
49686                 (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
49687               const CImg<tc>
49688                 _sprite = (sw!=color._width || sh!=color._height)?
49689                   color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
49690                 &sprite = _sprite?_sprite:color;
49691               const CImg<_to>
49692                 _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
49693                   _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
49694                 &nopacity = _nopacity?_nopacity:_opacity;
49695               draw_image(nx0,ny0,sprite,nopacity,g_opacity);
49696 
49697 #ifdef cimg_use_board
49698               if (pboard) {
49699                 board.setPenColorRGBi(128,128,128);
49700                 board.setFillColor(LibBoard::Color::Null);
49701                 board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
49702               }
49703 #endif
49704             }
49705           }
49706         } break;
49707         case 2 : { // Colored line
49708           const unsigned int
49709             n0 = (unsigned int)primitive[0],
49710             n1 = (unsigned int)primitive[1];
49711           const int
49712             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
49713             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
49714           const float
49715             z0 = vertices(n0,2) + Z + _focale,
49716             z1 = vertices(n1,2) + Z + _focale;
49717           if (render_type) {
49718             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
49719             else draw_line(x0,y0,x1,y1,pcolor,opacity);
49720 
49721 #ifdef cimg_use_board
49722             if (pboard) {
49723               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49724               board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1);
49725             }
49726 #endif
49727           } else {
49728             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
49729 
49730 #ifdef cimg_use_board
49731             if (pboard) {
49732               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49733               board.drawDot((float)x0,height() - (float)y0);
49734               board.drawDot((float)x1,height() - (float)y1);
49735             }
49736 #endif
49737           }
49738         } break;
49739         case 5 : { // Colored sphere
49740           const unsigned int
49741             n0 = (unsigned int)primitive[0],
49742             n1 = (unsigned int)primitive[1],
49743             is_wireframe = (unsigned int)primitive[2],
49744             is_radius = (unsigned int)primitive[3];
49745           float Xc,Yc,Zc,radius;
49746           if (is_radius) {
49747             Xc = (float)vertices(n0,0);
49748             Yc = (float)vertices(n0,1);
49749             Zc = (float)vertices(n0,2);
49750             radius = cimg::hypot(vertices(n1,0) - vertices(n0,0),
49751                                  vertices(n1,1) - vertices(n0,1),
49752                                  vertices(n1,2) - vertices(n0,2));
49753           } else {
49754             Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0));
49755             Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1));
49756             Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2));
49757             radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0),
49758                                       vertices(n1,1) - vertices(n0,1),
49759                                       vertices(n1,2) - vertices(n0,2));
49760           }
49761           const float
49762             zc = Z + Zc + _focale,
49763             af = absfocale?absfocale/zc:1,
49764             xc = X + Xc*af,
49765             yc = Y + Yc*af;
49766           radius*=af;
49767 
49768           switch (render_type) {
49769           case 0 :
49770             draw_point((int)xc,(int)yc,pcolor,opacity);
49771 
49772 #ifdef cimg_use_board
49773             if (pboard) {
49774               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49775               board.drawDot(xc,height() - yc);
49776             }
49777 #endif
49778             break;
49779           case 1 :
49780             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
49781 
49782 #ifdef cimg_use_board
49783             if (pboard) {
49784               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49785               board.setFillColor(LibBoard::Color::Null);
49786               board.drawCircle(xc,height() - yc,radius);
49787             }
49788 #endif
49789             break;
49790           default :
49791             if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
49792             else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
49793 
49794 #ifdef cimg_use_board
49795             if (pboard) {
49796               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49797               if (!is_wireframe) board.fillCircle(xc,height() - yc,radius);
49798               else {
49799                 board.setFillColor(LibBoard::Color::Null);
49800                 board.drawCircle(xc,height() - yc,radius);
49801               }
49802             }
49803 #endif
49804             break;
49805           }
49806         } break;
49807         case 6 : { // Textured line
49808           if (!__color) {
49809             if (render_type==5) cimg::mutex(10,0);
49810             throw CImgArgumentException(_cimg_instance
49811                                         "draw_object3d(): Undefined texture for line primitive [%u].",
49812                                         cimg_instance,n_primitive);
49813           }
49814           const unsigned int
49815             n0 = (unsigned int)primitive[0],
49816             n1 = (unsigned int)primitive[1];
49817           const int
49818             tx0 = (int)primitive[2], ty0 = (int)primitive[3],
49819             tx1 = (int)primitive[4], ty1 = (int)primitive[5],
49820             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
49821             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
49822           const float
49823             z0 = vertices(n0,2) + Z + _focale,
49824             z1 = vertices(n1,2) + Z + _focale;
49825           if (render_type) {
49826             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
49827             else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
49828 
49829 #ifdef cimg_use_board
49830             if (pboard) {
49831               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
49832               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
49833             }
49834 #endif
49835           } else {
49836             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
49837                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
49838               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
49839                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity);
49840 
49841 #ifdef cimg_use_board
49842             if (pboard) {
49843               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
49844               board.drawDot((float)x0,height() - (float)y0);
49845               board.drawDot((float)x1,height() - (float)y1);
49846             }
49847 #endif
49848           }
49849         } break;
49850         case 3 : { // Colored triangle
49851           const unsigned int
49852             n0 = (unsigned int)primitive[0],
49853             n1 = (unsigned int)primitive[1],
49854             n2 = (unsigned int)primitive[2];
49855           const int
49856             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
49857             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
49858             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
49859           const float
49860             z0 = vertices(n0,2) + Z + _focale,
49861             z1 = vertices(n1,2) + Z + _focale,
49862             z2 = vertices(n2,2) + Z + _focale;
49863           switch (render_type) {
49864           case 0 :
49865             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
49866 
49867 #ifdef cimg_use_board
49868             if (pboard) {
49869               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49870               board.drawDot((float)x0,height() - (float)y0);
49871               board.drawDot((float)x1,height() - (float)y1);
49872               board.drawDot((float)x2,height() - (float)y2);
49873             }
49874 #endif
49875             break;
49876           case 1 :
49877             if (zbuffer)
49878               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
49879                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
49880             else
49881               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
49882                 draw_line(x1,y1,x2,y2,pcolor,opacity);
49883 
49884 #ifdef cimg_use_board
49885             if (pboard) {
49886               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49887               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
49888               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
49889               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
49890             }
49891 #endif
49892             break;
49893           case 2 :
49894             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
49895             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
49896 
49897 #ifdef cimg_use_board
49898             if (pboard) {
49899               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49900               board.fillTriangle((float)x0,height() - (float)y0,
49901                                  (float)x1,height() - (float)y1,
49902                                  (float)x2,height() - (float)y2);
49903             }
49904 #endif
49905             break;
49906           case 3 :
49907             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
49908             else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
49909 
49910 #ifdef cimg_use_board
49911             if (pboard) {
49912               const float lp = std::min(lightprops(l),1.f);
49913               board.setPenColorRGBi((unsigned char)(color[0]*lp),
49914                                      (unsigned char)(color[1]*lp),
49915                                      (unsigned char)(color[2]*lp),
49916                                      (unsigned char)(opacity*255));
49917               board.fillTriangle((float)x0,height() - (float)y0,
49918                                  (float)x1,height() - (float)y1,
49919                                  (float)x2,height() - (float)y2);
49920             }
49921 #endif
49922             break;
49923           case 4 :
49924             if (zbuffer)
49925               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
49926                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
49927             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
49928 
49929 #ifdef cimg_use_board
49930             if (pboard) {
49931               board.setPenColorRGBi((unsigned char)(color[0]),
49932                                      (unsigned char)(color[1]),
49933                                      (unsigned char)(color[2]),
49934                                      (unsigned char)(opacity*255));
49935               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
49936                                          (float)x1,height() - (float)y1,lightprops(n1),
49937                                          (float)x2,height() - (float)y2,lightprops(n2));
49938             }
49939 #endif
49940             break;
49941           case 5 : {
49942             const unsigned int
49943               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
49944               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
49945               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1));
49946             if (zbuffer)
49947               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
49948             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
49949 
49950 #ifdef cimg_use_board
49951             if (pboard) {
49952               const float
49953                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
49954                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
49955                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
49956                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
49957                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
49958                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
49959               board.setPenColorRGBi((unsigned char)(color[0]),
49960                                      (unsigned char)(color[1]),
49961                                      (unsigned char)(color[2]),
49962                                      (unsigned char)(opacity*255));
49963               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
49964                                          (float)x1,height() - (float)y1,l1,
49965                                          (float)x2,height() - (float)y2,l2);
49966             }
49967 #endif
49968           } break;
49969           }
49970         } break;
49971         case 4 : { // Colored quadrangle
49972           const unsigned int
49973             n0 = (unsigned int)primitive[0],
49974             n1 = (unsigned int)primitive[1],
49975             n2 = (unsigned int)primitive[2],
49976             n3 = (unsigned int)primitive[3];
49977           const int
49978             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
49979             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
49980             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
49981             x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)),
49982             xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4;
49983           const float
49984             z0 = vertices(n0,2) + Z + _focale,
49985             z1 = vertices(n1,2) + Z + _focale,
49986             z2 = vertices(n2,2) + Z + _focale,
49987             z3 = vertices(n3,2) + Z + _focale,
49988             zc = (z0 + z1 + z2 + z3)/4;
49989 
49990           switch (render_type) {
49991           case 0 :
49992             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
49993               draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
49994 
49995 #ifdef cimg_use_board
49996             if (pboard) {
49997               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
49998               board.drawDot((float)x0,height() - (float)y0);
49999               board.drawDot((float)x1,height() - (float)y1);
50000               board.drawDot((float)x2,height() - (float)y2);
50001               board.drawDot((float)x3,height() - (float)y3);
50002             }
50003 #endif
50004             break;
50005           case 1 :
50006             if (zbuffer)
50007               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
50008                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
50009             else
50010               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
50011                 draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
50012 
50013 #ifdef cimg_use_board
50014             if (pboard) {
50015               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50016               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50017               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
50018               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
50019               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
50020             }
50021 #endif
50022             break;
50023           case 2 :
50024             if (zbuffer)
50025               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
50026                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
50027             else
50028               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
50029 
50030 #ifdef cimg_use_board
50031             if (pboard) {
50032               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
50033               board.fillTriangle((float)x0,height() - (float)y0,
50034                                  (float)x1,height() - (float)y1,
50035                                  (float)x2,height() - (float)y2);
50036               board.fillTriangle((float)x0,height() - (float)y0,
50037                                  (float)x2,height() - (float)y2,
50038                                  (float)x3,height() - (float)y3);
50039             }
50040 #endif
50041             break;
50042           case 3 :
50043             if (zbuffer)
50044               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
50045                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
50046             else
50047               _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
50048                 _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
50049 
50050 #ifdef cimg_use_board
50051             if (pboard) {
50052               const float lp = std::min(lightprops(l),1.f);
50053               board.setPenColorRGBi((unsigned char)(color[0]*lp),
50054                                      (unsigned char)(color[1]*lp),
50055                                      (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
50056               board.fillTriangle((float)x0,height() - (float)y0,
50057                                  (float)x1,height() - (float)y1,
50058                                  (float)x2,height() - (float)y2);
50059               board.fillTriangle((float)x0,height() - (float)y0,
50060                                  (float)x2,height() - (float)y2,
50061                                  (float)x3,height() - (float)y3);
50062             }
50063 #endif
50064             break;
50065           case 4 : {
50066             const float
50067               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
50068               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3),
50069               lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4;
50070             if (zbuffer)
50071               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
50072               draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
50073               draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
50074                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
50075             else
50076               draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
50077               draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
50078               draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
50079                 draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
50080 
50081 #ifdef cimg_use_board
50082             if (pboard) {
50083               board.setPenColorRGBi((unsigned char)(color[0]),
50084                                      (unsigned char)(color[1]),
50085                                      (unsigned char)(color[2]),
50086                                      (unsigned char)(opacity*255));
50087               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
50088                                          (float)x1,height() - (float)y1,lightprop1,
50089                                          (float)x2,height() - (float)y2,lightprop2);
50090               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
50091                                          (float)x2,height() - (float)y2,lightprop2,
50092                                          (float)x3,height() - (float)y3,lightprop3);
50093             }
50094 #endif
50095           } break;
50096           case 5 : {
50097             const unsigned int
50098               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
50099               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
50100               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
50101               lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)),
50102               lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4;
50103             if (zbuffer)
50104               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
50105               draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
50106               draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
50107                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
50108             else
50109               draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
50110               draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
50111               draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
50112                 draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
50113 
50114 #ifdef cimg_use_board
50115             if (pboard) {
50116               const float
50117                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
50118                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
50119                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
50120                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
50121               board.setPenColorRGBi((unsigned char)(color[0]),
50122                                      (unsigned char)(color[1]),
50123                                      (unsigned char)(color[2]),
50124                                      (unsigned char)(opacity*255));
50125               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50126                                          (float)x1,height() - (float)y1,l1,
50127                                          (float)x2,height() - (float)y2,l2);
50128               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50129                                          (float)x2,height() - (float)y2,l2,
50130                                          (float)x3,height() - (float)y3,l3);
50131             }
50132 #endif
50133           } break;
50134           }
50135         } break;
50136         case 9 : { // Textured triangle
50137           if (!__color) {
50138             if (render_type==5) cimg::mutex(10,0);
50139             throw CImgArgumentException(_cimg_instance
50140                                         "draw_object3d(): Undefined texture for triangle primitive [%u].",
50141                                         cimg_instance,n_primitive);
50142           }
50143           const unsigned int
50144             n0 = (unsigned int)primitive[0],
50145             n1 = (unsigned int)primitive[1],
50146             n2 = (unsigned int)primitive[2];
50147           const int
50148             tx0 = (int)primitive[3], ty0 = (int)primitive[4],
50149             tx1 = (int)primitive[5], ty1 = (int)primitive[6],
50150             tx2 = (int)primitive[7], ty2 = (int)primitive[8],
50151             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50152             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
50153             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
50154           const float
50155             z0 = vertices(n0,2) + Z + _focale,
50156             z1 = vertices(n1,2) + Z + _focale,
50157             z2 = vertices(n2,2) + Z + _focale;
50158           switch (render_type) {
50159           case 0 :
50160             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
50161                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
50162               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
50163                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
50164               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
50165                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity);
50166 #ifdef cimg_use_board
50167             if (pboard) {
50168               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50169               board.drawDot((float)x0,height() - (float)y0);
50170               board.drawDot((float)x1,height() - (float)y1);
50171               board.drawDot((float)x2,height() - (float)y2);
50172             }
50173 #endif
50174             break;
50175           case 1 :
50176             if (zbuffer)
50177               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
50178                 draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
50179                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
50180             else
50181               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
50182                 draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
50183                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
50184 
50185 #ifdef cimg_use_board
50186             if (pboard) {
50187               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50188               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50189               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
50190               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
50191             }
50192 #endif
50193             break;
50194           case 2 :
50195             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
50196             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
50197 
50198 #ifdef cimg_use_board
50199             if (pboard) {
50200               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50201               board.fillTriangle((float)x0,height() - (float)y0,
50202                                  (float)x1,height() - (float)y1,
50203                                  (float)x2,height() - (float)y2);
50204             }
50205 #endif
50206             break;
50207           case 3 :
50208             if (zbuffer)
50209               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
50210             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
50211 
50212 #ifdef cimg_use_board
50213             if (pboard) {
50214               const float lp = std::min(lightprops(l),1.f);
50215               board.setPenColorRGBi((unsigned char)(128*lp),
50216                                     (unsigned char)(128*lp),
50217                                     (unsigned char)(128*lp),
50218                                     (unsigned char)(opacity*255));
50219               board.fillTriangle((float)x0,height() - (float)y0,
50220                                  (float)x1,height() - (float)y1,
50221                                  (float)x2,height() - (float)y2);
50222             }
50223 #endif
50224             break;
50225           case 4 :
50226             if (zbuffer)
50227               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50228                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
50229             else
50230               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50231                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
50232 
50233 #ifdef cimg_use_board
50234             if (pboard) {
50235               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50236               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
50237                                         (float)x1,height() - (float)y1,lightprops(n1),
50238                                         (float)x2,height() - (float)y2,lightprops(n2));
50239             }
50240 #endif
50241             break;
50242           case 5 :
50243             if (zbuffer)
50244               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
50245                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
50246                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
50247                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
50248                             opacity);
50249             else
50250               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
50251                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
50252                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
50253                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
50254                             opacity);
50255 
50256 #ifdef cimg_use_board
50257             if (pboard) {
50258               const float
50259                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
50260                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
50261                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
50262                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
50263                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
50264                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
50265               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50266               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50267                                         (float)x1,height() - (float)y1,l1,
50268                                         (float)x2,height() - (float)y2,l2);
50269             }
50270 #endif
50271             break;
50272           }
50273         } break;
50274         case 12 : { // Textured quadrangle
50275           if (!__color) {
50276             if (render_type==5) cimg::mutex(10,0);
50277             throw CImgArgumentException(_cimg_instance
50278                                         "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
50279                                         cimg_instance,n_primitive);
50280           }
50281           const unsigned int
50282             n0 = (unsigned int)primitive[0],
50283             n1 = (unsigned int)primitive[1],
50284             n2 = (unsigned int)primitive[2],
50285             n3 = (unsigned int)primitive[3];
50286           const int
50287             tx0 = (int)primitive[4], ty0 = (int)primitive[5],
50288             tx1 = (int)primitive[6], ty1 = (int)primitive[7],
50289             tx2 = (int)primitive[8], ty2 = (int)primitive[9],
50290             tx3 = (int)primitive[10], ty3 = (int)primitive[11],
50291             x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
50292             x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
50293             x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
50294             x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1));
50295           const float
50296             z0 = vertices(n0,2) + Z + _focale,
50297             z1 = vertices(n1,2) + Z + _focale,
50298             z2 = vertices(n2,2) + Z + _focale,
50299             z3 = vertices(n3,2) + Z + _focale;
50300 
50301           switch (render_type) {
50302           case 0 :
50303             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
50304                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
50305               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
50306                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
50307               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
50308                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity).
50309               draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3,
50310                                                    ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity);
50311 
50312 #ifdef cimg_use_board
50313             if (pboard) {
50314               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50315               board.drawDot((float)x0,height() - (float)y0);
50316               board.drawDot((float)x1,height() - (float)y1);
50317               board.drawDot((float)x2,height() - (float)y2);
50318               board.drawDot((float)x3,height() - (float)y3);
50319             }
50320 #endif
50321             break;
50322           case 1 :
50323             if (zbuffer)
50324               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
50325                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
50326                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
50327                 draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
50328             else
50329               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
50330                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
50331                 draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
50332                 draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
50333 
50334 #ifdef cimg_use_board
50335             if (pboard) {
50336               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50337               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
50338               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
50339               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
50340               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
50341             }
50342 #endif
50343             break;
50344           case 2 :
50345             if (zbuffer)
50346               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
50347                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
50348             else
50349               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
50350                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
50351 
50352 #ifdef cimg_use_board
50353             if (pboard) {
50354               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50355               board.fillTriangle((float)x0,height() - (float)y0,
50356                                  (float)x1,height() - (float)y1,
50357                                  (float)x2,height() - (float)y2);
50358               board.fillTriangle((float)x0,height() - (float)y0,
50359                                  (float)x2,height() - (float)y2,
50360                                  (float)x3,height() - (float)y3);
50361             }
50362 #endif
50363             break;
50364           case 3 :
50365             if (zbuffer)
50366               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
50367                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
50368             else
50369               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
50370                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
50371 
50372 #ifdef cimg_use_board
50373             if (pboard) {
50374               const float lp = std::min(lightprops(l),1.f);
50375               board.setPenColorRGBi((unsigned char)(128*lp),
50376                                      (unsigned char)(128*lp),
50377                                      (unsigned char)(128*lp),
50378                                      (unsigned char)(opacity*255));
50379               board.fillTriangle((float)x0,height() - (float)y0,
50380                                  (float)x1,height() - (float)y1,
50381                                  (float)x2,height() - (float)y2);
50382               board.fillTriangle((float)x0,height() - (float)y0,
50383                                  (float)x2,height() - (float)y2,
50384                                  (float)x3,height() - (float)y3);
50385             }
50386 #endif
50387             break;
50388           case 4 : {
50389             const float
50390               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
50391               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
50392             if (zbuffer)
50393               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50394                             lightprop0,lightprop1,lightprop2,opacity).
50395                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
50396                               lightprop0,lightprop2,lightprop3,opacity);
50397             else
50398               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50399                             lightprop0,lightprop1,lightprop2,opacity).
50400                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
50401                               lightprop0,lightprop2,lightprop3,opacity);
50402 
50403 #ifdef cimg_use_board
50404             if (pboard) {
50405               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50406               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
50407                                          (float)x1,height() - (float)y1,lightprop1,
50408                                          (float)x2,height() - (float)y2,lightprop2);
50409               board.fillGouraudTriangle((float)x0,height()  -(float)y0,lightprop0,
50410                                          (float)x2,height() - (float)y2,lightprop2,
50411                                          (float)x3,height() - (float)y3,lightprop3);
50412             }
50413 #endif
50414           } break;
50415           case 5 : {
50416             const unsigned int
50417               lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
50418               lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
50419               lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
50420               lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1));
50421             if (zbuffer)
50422               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50423                             light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
50424                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
50425                             light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
50426             else
50427               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
50428                             light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
50429                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
50430                             light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
50431 #ifdef cimg_use_board
50432             if (pboard) {
50433               const float
50434                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
50435                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
50436                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
50437                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
50438               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
50439               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
50440                                          (float)x1,height() - (float)y1,l1,
50441                                          (float)x2,height() - (float)y2,l2);
50442               board.fillGouraudTriangle((float)x0,height()  -(float)y0,l0,
50443                                          (float)x2,height() - (float)y2,l2,
50444                                          (float)x3,height() - (float)y3,l3);
50445             }
50446 #endif
50447           } break;
50448           }
50449         } break;
50450         }
50451       }
50452       if (render_type==5) cimg::mutex(10,0);
50453       return *this;
50454     }
50455 
50456     //@}
50457     //---------------------------
50458     //
50459     //! \name Data Input
50460     //@{
50461     //---------------------------
50462 
50463     //! Launch simple interface to select a shape from an image.
50464     /**
50465        \param disp Display window to use.
50466        \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
50467        \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
50468        \param exit_on_anykey Exit function when any key is pressed.
50469     **/
50470     CImg<T>& select(CImgDisplay &disp,
50471                     const unsigned int feature_type=2, unsigned int *const XYZ=0,
50472                     const bool exit_on_anykey=false,
50473                     const bool is_deep_selection_default=false) {
50474       return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
50475     }
50476 
50477     //! Simple interface to select a shape from an image \overloading.
50478     CImg<T>& select(const char *const title,
50479                     const unsigned int feature_type=2, unsigned int *const XYZ=0,
50480                     const bool exit_on_anykey=false,
50481                     const bool is_deep_selection_default=false) {
50482       return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
50483     }
50484 
50485     //! Simple interface to select a shape from an image \newinstance.
50486     CImg<intT> get_select(CImgDisplay &disp,
50487                           const unsigned int feature_type=2, unsigned int *const XYZ=0,
50488                           const bool exit_on_anykey=false,
50489                           const bool is_deep_selection_default=false) const {
50490       return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
50491     }
50492 
50493     //! Simple interface to select a shape from an image \newinstance.
50494     CImg<intT> get_select(const char *const title,
50495                           const unsigned int feature_type=2, unsigned int *const XYZ=0,
50496                           const bool exit_on_anykey=false,
50497                           const bool is_deep_selection_default=false) const {
50498       CImgDisplay disp;
50499       return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
50500     }
50501 
50502     CImg<intT> _select(CImgDisplay &disp, const char *const title,
50503                        const unsigned int feature_type, unsigned int *const XYZ,
50504                        const int origX, const int origY, const int origZ,
50505                        const bool exit_on_anykey,
50506                        const bool reset_view3d,
50507                        const bool force_display_z_coord,
50508                        const bool is_deep_selection_default) const {
50509       if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
50510       if (!disp) {
50511         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
50512         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
50513       } else {
50514         if (title) disp.set_title("%s",title);
50515         disp.move_inside_screen();
50516       }
50517 
50518       CImg<T> thumb;
50519       if (width()>disp.screen_width() || height()>disp.screen_height())
50520         get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb);
50521 
50522       const unsigned int old_normalization = disp.normalization();
50523       bool old_is_resized = disp.is_resized();
50524       disp._normalization = 0;
50525       disp.show().set_key(0).set_wheel().show_mouse();
50526 
50527       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
50528       int area = 0, area_started = 0, area_clicked = 0, phase = 0,
50529         X0 = (int)((XYZ?XYZ[0]:_width/2)%_width),
50530         Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height),
50531         Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
50532         X1 =-1, Y1 = -1, Z1 = -1,
50533         X3d = -1, Y3d = -1,
50534         oX3d = X3d, oY3d = -1,
50535         omx = -1, omy = -1;
50536       float X = -1, Y = -1, Z = -1;
50537       unsigned int key = 0, font_size = 32;
50538 
50539       bool is_deep_selection = is_deep_selection_default,
50540         shape_selected = false, text_down = false, visible_cursor = true;
50541       static CImg<floatT> pose3d;
50542       static bool is_view3d = false, is_axes = true;
50543       if (reset_view3d) { pose3d.assign(); is_view3d = false; }
50544       CImg<floatT> points3d, opacities3d, sel_opacities3d;
50545       CImgList<uintT> primitives3d, sel_primitives3d;
50546       CImgList<ucharT> colors3d, sel_colors3d;
50547       CImg<ucharT> visu, visu0, view3d;
50548       CImg<charT> text(1024); *text = 0;
50549 
50550       while (!key && !disp.is_closed() && !shape_selected) {
50551 
50552         // Handle mouse motion and selection
50553         int
50554           mx = disp.mouse_x(),
50555           my = disp.mouse_y();
50556 
50557         const float
50558           mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(),
50559           mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height();
50560 
50561         area = 0;
50562         if (mX>=0 && mY>=0 && mX<width() && mY<height())  { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
50563         if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
50564         if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
50565         if (mX>=width() && mY>=height()) area = 4;
50566         if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0;
50567 
50568         CImg<charT> filename(32);
50569 
50570         switch (key = disp.key()) {
50571 #if cimg_OS!=2
50572         case cimg::keyCTRLRIGHT :
50573 #endif
50574         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
50575         case cimg::keyPAGEUP :
50576           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
50577         case cimg::keyPAGEDOWN :
50578           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
50579         case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
50580             is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
50581           } break;
50582         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
50583             disp.set_fullscreen(false).
50584               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
50585                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
50586               _is_resized = true;
50587             disp.set_key(key,false); key = 0; visu0.assign();
50588           } break;
50589         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
50590             disp.set_fullscreen(false).
50591               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
50592             disp.set_key(key,false); key = 0; visu0.assign();
50593           } break;
50594         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
50595             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
50596             disp.set_key(key,false); key = 0; visu0.assign();
50597           } break;
50598         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
50599             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
50600             disp.set_key(key,false); key = 0; visu0.assign();
50601           } break;
50602         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
50603             is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
50604           } break;
50605         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
50606             static unsigned int snap_number = 0;
50607             std::FILE *file;
50608             do {
50609               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
50610               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
50611             } while (file);
50612             if (visu0) {
50613               (+visu0).__draw_text(" Saving snapshot...",font_size,(int)text_down).display(disp);
50614               visu0.save(filename);
50615               (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
50616             }
50617             disp.set_key(key,false); key = 0;
50618           } break;
50619         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
50620             static unsigned int snap_number = 0;
50621             std::FILE *file;
50622             do {
50623 
50624 #ifdef cimg_use_zlib
50625               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
50626 #else
50627               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
50628 #endif
50629               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
50630             } while (file);
50631             (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
50632             save(filename);
50633             (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
50634             disp.set_key(key,false); key = 0;
50635           } break;
50636         }
50637 
50638         switch (area) {
50639 
50640         case 0 : // When mouse is out of image range
50641           mx = my = -1; X = Y = Z = -1;
50642           break;
50643 
50644         case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections
50645           const unsigned int but = disp.button();
50646           const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4);
50647 
50648           if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step)
50649             if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
50650             X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
50651           }
50652           if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes)
50653             switch (area_started) {
50654             case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
50655             case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
50656             case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
50657             }
50658           }
50659           if (b2 && area_clicked==area) { // When moving through the image/volume
50660             if (phase) {
50661               if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
50662               X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
50663             } else {
50664               if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
50665               X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
50666             }
50667           }
50668           if (b3) { // Reset selection
50669             X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0;
50670             visu0.assign();
50671           }
50672           if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel)
50673             if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
50674                 !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) {
50675               switch (area) {
50676               case 1 :
50677                 if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
50678                 visu0.assign(); break;
50679               case 2 :
50680                 if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
50681                 visu0.assign(); break;
50682               case 3 :
50683                 if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
50684                 visu0.assign(); break;
50685               }
50686               disp.set_wheel();
50687             } else key = ~0U;
50688           }
50689 
50690           if ((phase==0 && b1) ||
50691               (phase==1 && !b1) ||
50692               (phase==2 && b1)) switch (phase) { // Detect change of phase
50693             case 0 :
50694               if (area==area_clicked) {
50695                 X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase;
50696               } break;
50697             case 1 :
50698               if (area==area_started) {
50699                 X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
50700                 if (_depth>1) {
50701                   if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default;
50702                   if (is_deep_selection) ++phase;
50703                 }
50704               } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
50705               break;
50706             case 2 : ++phase; break;
50707             }
50708         } break;
50709 
50710         case 4 : // When mouse is over the 3D view
50711           if (is_view3d && points3d) {
50712             X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0));
50713             Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0));
50714             if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
50715             // Left + right buttons: reset.
50716             if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
50717             else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate
50718               const float
50719                 R = 0.45f*std::min(view3d._width,view3d._height),
50720                 R2 = R*R,
50721                 u0 = (float)(oX3d - view3d.width()/2),
50722                 v0 = (float)(oY3d - view3d.height()/2),
50723                 u1 = (float)(X3d - view3d.width()/2),
50724                 v1 = (float)(Y3d - view3d.height()/2),
50725                 n0 = cimg::hypot(u0,v0),
50726                 n1 = cimg::hypot(u1,v1),
50727                 nu0 = n0>R?(u0*R/n0):u0,
50728                 nv0 = n0>R?(v0*R/n0):v0,
50729                 nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
50730                 nu1 = n1>R?(u1*R/n1):u1,
50731                 nv1 = n1>R?(v1*R/n1):v1,
50732                 nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
50733                 u = nv0*nw1 - nw0*nv1,
50734                 v = nw0*nu1 - nu0*nw1,
50735                 w = nv0*nu1 - nu0*nv1,
50736                 n = cimg::hypot(u,v,w),
50737                 alpha = (float)std::asin(n/R2)*180/cimg::PI;
50738               pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2));
50739               view3d.assign();
50740             } else if (disp.button()&2 && pose3d && oY3d!=Y3d) {  // Right button: zoom
50741               pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign();
50742             }
50743             if (disp.wheel()) { // Wheel: zoom
50744               pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
50745             }
50746             if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift
50747               pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
50748             }
50749             oX3d = X3d; oY3d = Y3d;
50750           }
50751           mx = my = -1; X = Y = Z = -1;
50752           break;
50753         }
50754 
50755         if (phase) {
50756           if (!feature_type) shape_selected = phase?true:false;
50757           else {
50758             if (_depth>1) shape_selected = (phase==3)?true:false;
50759             else shape_selected = (phase==2)?true:false;
50760           }
50761         }
50762 
50763         if (X0<0) X0 = 0;
50764         if (X0>=width()) X0 = width() - 1;
50765         if (Y0<0) Y0 = 0;
50766         if (Y0>=height()) Y0 = height() - 1;
50767         if (Z0<0) Z0 = 0;
50768         if (Z0>=depth()) Z0 = depth() - 1;
50769         if (X1<1) X1 = 0;
50770         if (X1>=width()) X1 = width() - 1;
50771         if (Y1<0) Y1 = 0;
50772         if (Y1>=height()) Y1 = height() - 1;
50773         if (Z1<0) Z1 = 0;
50774         if (Z1>=depth()) Z1 = depth() - 1;
50775 
50776         // Draw visualization image on the display
50777         if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
50778 
50779           if (!visu0) { // Create image of projected planes
50780             if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
50781             else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
50782             visu0.resize(disp);
50783             view3d.assign();
50784             points3d.assign();
50785           }
50786 
50787           if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images
50788             const unsigned int
50789               _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1),
50790               _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1),
50791               x3d = _x3d>=visu0._width?visu0._width - 1:_x3d,
50792               y3d = _y3d>=visu0._height?visu0._height - 1:_y3d;
50793             CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3).
50794               move_to(view3d);
50795             if (!points3d) {
50796               get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
50797               points3d.append(CImg<floatT>(8,3,1,1,
50798                                            0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0,
50799                                            0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1,
50800                                            0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x');
50801               CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
50802               CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
50803               CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
50804               CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
50805               CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
50806               CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
50807               colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
50808               opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
50809               if (!phase) {
50810                 opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
50811                 sel_primitives3d.assign();
50812                 sel_colors3d.assign();
50813                 sel_opacities3d.assign();
50814               } else {
50815                 if (feature_type==2) {
50816                   points3d.append(CImg<floatT>(8,3,1,1,
50817                                                X0,X1,X1,X0,X0,X1,X1,X0,
50818                                                Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
50819                                                Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
50820                   sel_primitives3d.assign();
50821                   CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
50822                   CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
50823                   CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
50824                   CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
50825                   CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
50826                   CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
50827                   CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
50828                   CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
50829                   CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
50830                   CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
50831                   CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
50832                   CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
50833                 } else {
50834                   points3d.append(CImg<floatT>(2,3,1,1,
50835                                                X0,X1,
50836                                                Y0,Y1,
50837                                                Z0,Z1),'x');
50838                   sel_primitives3d.assign(CImg<uintT>::vector(20,21));
50839                 }
50840                 sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
50841                 sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
50842               }
50843               points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d();
50844               points3d*=0.75f*std::min(view3d._width,view3d._height);
50845             }
50846 
50847             if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
50848             CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
50849             const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
50850             if (sel_primitives3d)
50851               view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
50852                                    pose3d(3,1) + 0.5f*view3d._height,
50853                                    pose3d(3,2),
50854                                    rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
50855                                    2,true,500,0,0,0,0,0,1,zbuffer3d);
50856             view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
50857                                  pose3d(3,1) + 0.5f*view3d._height,
50858                                  pose3d(3,2),
50859                                  rotated_points3d,primitives3d,colors3d,opacities3d,
50860                                  2,true,500,0,0,0,0,0,1,zbuffer3d);
50861             visu0.draw_image(x3d,y3d,view3d);
50862           }
50863           visu = visu0;
50864 
50865           if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
50866           else {
50867             if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
50868             else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
50869             const int d = (depth()>1)?depth():0;
50870             int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z;
50871             if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; }
50872             int
50873               w = disp.width(), W = width() + d,
50874               h = disp.height(), H = height() + d,
50875               _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX),
50876               _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY),
50877               _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1),
50878               _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1),
50879               _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()),
50880               _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()),
50881               _zxn = (int)((_vZ + width() + 1.f)*w/W - 1),
50882                        zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1),
50883               _zyn = (int)((_vZ + height() + 1.f)*h/H - 1),
50884                        zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1),
50885               _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()),
50886               _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()),
50887               xc = (xp + xn)/2,
50888               yc = (yp + yn)/2,
50889               zxc = (zxp + zxn)/2,
50890               zyc = (zyp + zyn)/2,
50891               xf = (int)(X*w/W),
50892               yf = (int)(Y*h/H),
50893               zxf = (int)((Z + width())*w/W),
50894               zyf = (int)((Z + height())*h/H);
50895 
50896             if (is_axes) { // Draw axes
50897               visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00).
50898                 draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF).
50899                 draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00).
50900                 draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF);
50901               if (_depth>1)
50902                 visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
50903                   draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
50904                   draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
50905                   draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
50906             }
50907 
50908             // Draw box cursor.
50909             if (xn - xp>=4 && yn - yp>=4)
50910               visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
50911                 draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
50912                 draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
50913             if (_depth>1) {
50914               if (yn - yp>=4 && zxn - zxp>=4)
50915                 visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
50916                                               draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
50917                                               draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
50918               if (xn - xp>=4 && zyn - zyp>=4)
50919                 visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
50920                           draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
50921                           draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
50922             }
50923 
50924             // Draw selection.
50925             if (phase && (phase!=1 || area_started==area)) {
50926               const int
50927                 _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0),
50928                 _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0),
50929                 _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1),
50930                 _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1),
50931                 _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()),
50932                 _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()),
50933                 _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1),
50934                 zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1),
50935                 _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1),
50936                 zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1),
50937                 xc0 = (xp0 + xn0)/2,
50938                 yc0 = (yp0 + yn0)/2,
50939                 zxc0 = (zxp0 + zxn0)/2,
50940                 zyc0 = (zyp0 + zyn0)/2;
50941 
50942               switch (feature_type) {
50943               case 1 : { // Vector
50944                 visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x33333333).
50945                   draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC);
50946                 if (d) {
50947                   visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x33333333).
50948                     draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC).
50949                     draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x33333333).
50950                     draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xCCCCCCCC);
50951                 }
50952               } break;
50953               case 2 : { // Box
50954                 visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
50955                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555).
50956                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA);
50957                 if (xc0!=xc && yc0!=yc)
50958                   visu.draw_line(xc0,yc0,xc,yc,background_color,0.9f,0x33333333).
50959                     draw_line(xc0,yc0,xc,yc,foreground_color,0.9f,0xCCCCCCCC);
50960                 if (d) {
50961                   visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
50962                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
50963                                    background_color,0.9f,0x55555555).
50964                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
50965                                    foreground_color,0.9f,0xAAAAAAAA);
50966                   if (zxc0!=zxc && yc0!=yc)
50967                     visu.draw_line(zxc0,yc0,zxc,yc,background_color,0.9f,0x33333333).
50968                       draw_line(zxc0,yc0,zxc,yc,foreground_color,0.9f,0xCCCCCCCC);
50969                   visu.draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
50970                                       background_color,0.2f).
50971                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
50972                                    background_color,0.9f,0x55555555).
50973                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
50974                                    foreground_color,0.9f,0xAAAAAAAA);
50975                   if (xp0!=xn && zyp0!=zyn)
50976                     visu.draw_line(xp0,zyp0,xn,zyn,background_color,0.9f,0x33333333).
50977                       draw_line(xp0,zyp0,xn,zyn,foreground_color,0.9f,0xCCCCCCCC);
50978                 }
50979               } break;
50980               case 3 : { // Ellipse
50981                 visu.draw_ellipse(xc0,yc0,
50982                                   (float)cimg::abs(xc - xc0),
50983                                   (float)cimg::abs(yc - yc0),0,background_color,0.2f).
50984                   draw_ellipse(xc0,yc0,
50985                                (float)cimg::abs(xc - xc0),
50986                                (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U).
50987                   draw_point(xc0,yc0,foreground_color,0.9f);
50988                 if (d) {
50989                   visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
50990                                     background_color,0.2f).
50991                     draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
50992                                  foreground_color,0.9f,~0U).
50993                     draw_point(zxc0,yc0,foreground_color,0.9f).
50994                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
50995                                  background_color,0.2f).
50996                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
50997                                  foreground_color,0.9f,~0U).
50998                     draw_point(xc0,zyc0,foreground_color,0.9f);
50999                 }
51000               } break;
51001               }
51002             }
51003 
51004             // Draw text info.
51005             if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false;
51006             if (!feature_type || !phase) {
51007               if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
51008                 if (_depth>1 || force_display_z_coord)
51009                   cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z);
51010                 else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y);
51011                 CImg<T> values = get_vector_at((int)X,(int)Y,(int)Z);
51012                 const bool is_large_spectrum = values._height>8;
51013                 if (is_large_spectrum)
51014                   values.draw_image(0,4,values.get_rows(values._height - 4,values._height - 1)).resize(1,8,1,1,0);
51015                 char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512;
51016                 for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) {
51017                   cimg_snprintf(ctext,24,cimg::type<T>::format_s(),
51018                                 cimg::type<T>::format(values[c]));
51019                   ctext += std::strlen(ctext);
51020                   if (c==3 && is_large_spectrum) {
51021                     cimg_snprintf(ctext,24," ...");
51022                     ctext += std::strlen(ctext);
51023                   }
51024                   *(ctext++) = ' '; *ctext = 0;
51025                 }
51026                 std::strcpy(text._data + std::strlen(text),"] ");
51027               }
51028             } else switch (feature_type) {
51029               case 1 : {
51030                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
51031                   length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
51032                 if (_depth>1 || force_display_z_coord)
51033                   cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ",
51034                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length);
51035                 else if (_width!=1 && _height!=1)
51036                   cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ",
51037                                 origX + X0,origY + Y0,origX + X1,origY + Y1,length,
51038                                 cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
51039                 else
51040                   cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ",
51041                                 origX + X0,origY + Y0,origX + X1,origY + Y1,length);
51042               } break;
51043               case 2 : {
51044                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
51045                   length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
51046                 if (_depth>1 || force_display_z_coord)
51047                   cimg_snprintf(text,text._width,
51048                                 " Box ( %d,%d,%d ) - ( %d,%d,%d )\n Size = ( %d,%d,%d ), Length = %g ",
51049                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1),
51050                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0),
51051                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length);
51052                 else if (_width!=1 && _height!=1)
51053                   cimg_snprintf(text,text._width,
51054                                 " Box ( %d,%d ) - ( %d,%d )\n Size = ( %d,%d ), Length = %g \n Angle = %g\260 ",
51055                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
51056                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
51057                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length,
51058                                 cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
51059                 else
51060                   cimg_snprintf(text,text._width,
51061                                 " Box ( %d,%d ) - ( %d,%d )\n Size = (%d,%d), Length = %g ",
51062                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
51063                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
51064                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length);
51065               } break;
51066               default :
51067                 if (_depth>1 || force_display_z_coord)
51068                   cimg_snprintf(text,text._width," Ellipse ( %d,%d,%d ) - ( %d,%d,%d ), Radii = ( %d,%d,%d ) ",
51069                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,
51070                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1));
51071                 else cimg_snprintf(text,text._width," Ellipse ( %d,%d ) - ( %d,%d ), Radii = ( %d,%d ) ",
51072                                    origX + X0,origY + Y0,origX + X1,origY + Y1,
51073                                    1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1));
51074               }
51075             if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",font_size,(int)text_down,text._data);
51076           }
51077 
51078           disp.display(visu);
51079         }
51080         if (!shape_selected) disp.wait();
51081         if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
51082         omx = mx; omy = my;
51083         if (!exit_on_anykey && key && key!=cimg::keyESC &&
51084             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
51085           key = 0;
51086         }
51087       }
51088 
51089       // Return result.
51090       CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
51091       if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
51092       if (shape_selected) {
51093         if (feature_type==2) {
51094           if (is_deep_selection) switch (area_started) {
51095             case 1 : Z0 = 0; Z1 = _depth - 1; break;
51096             case 2 : Y0 = 0; Y1 = _height - 1; break;
51097             case 3 : X0 = 0; X1 = _width - 1; break;
51098           }
51099           if (X0>X1) cimg::swap(X0,X1);
51100           if (Y0>Y1) cimg::swap(Y0,Y1);
51101           if (Z0>Z1) cimg::swap(Z0,Z1);
51102         }
51103         if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
51104         switch (feature_type) {
51105         case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
51106         case 3 :
51107           res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0);
51108           res[0] = X0; res[1] = Y0; res[2] = Z0;
51109           break;
51110         default : res[0] = X0; res[1] = Y0; res[2] = Z0;
51111         }
51112       }
51113       if (!exit_on_anykey || !(disp.button()&4)) disp.set_button();
51114       if (!visible_cursor) disp.show_mouse();
51115       disp._normalization = old_normalization;
51116       disp._is_resized = old_is_resized;
51117       if (key!=~0U) disp.set_key(key);
51118       return res;
51119     }
51120 
51121     // Return a visualizable uchar8 image for display routines.
51122     CImg<ucharT> _get_select(const CImgDisplay& disp, const int normalization,
51123                              const int x, const int y, const int z) const {
51124       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
51125       const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1));
51126       CImg<Tuchar> img2d;
51127       if (_depth>1) {
51128         const int mdisp = std::min(disp.screen_width(),disp.screen_height());
51129         if (depth()>mdisp) {
51130           crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d);
51131           img2d.projections2d(x,y,z*img2d._depth/_depth);
51132         } else crop.get_projections2d(x,y,z).move_to(img2d);
51133       } else CImg<Tuchar>(crop,false).move_to(img2d);
51134 
51135       // Check for inf and NaN values.
51136       if (cimg::type<T>::is_float() && normalization) {
51137         bool is_inf = false, is_nan = false;
51138         cimg_for(img2d,ptr,Tuchar)
51139           if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
51140           else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
51141         if (is_inf || is_nan) {
51142           Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min();
51143           if (!normalization) { m0 = 0; M0 = 255; }
51144           else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; }
51145           else
51146             cimg_for(img2d,ptr,Tuchar)
51147               if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
51148                 if (*ptr<(Tuchar)m0) m0 = *ptr;
51149                 if (*ptr>(Tuchar)M0) M0 = *ptr;
51150               }
51151           const T
51152             val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0),
51153             val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0);
51154           if (is_nan)
51155             cimg_for(img2d,ptr,Tuchar)
51156               if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values
51157           if (is_inf)
51158             cimg_for(img2d,ptr,Tuchar)
51159               if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values
51160         }
51161       }
51162 
51163       switch (normalization) {
51164       case 1 : img2d.normalize((ucharT)0,(ucharT)255); break;
51165       case 2 : {
51166         const float m = disp._min, M = disp._max;
51167         (img2d-=m)*=255.f/(M - m>0?M - m:1);
51168       } break;
51169       case 3 :
51170         if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255);
51171         else {
51172           const float
51173             m = (float)cimg::type<T>::min(),
51174             M = (float)cimg::type<T>::max();
51175           (img2d-=m)*=255.f/(M - m>0?M - m:1);
51176         } break;
51177       }
51178       if (img2d.spectrum()==2) img2d.channels(0,2);
51179       return img2d;
51180     }
51181 
51182     //! Select sub-graph in a graph.
51183     CImg<intT> get_select_graph(CImgDisplay &disp,
51184                                 const unsigned int plot_type=1, const unsigned int vertex_type=1,
51185                                 const char *const labelx=0, const double xmin=0, const double xmax=0,
51186                                 const char *const labely=0, const double ymin=0, const double ymax=0,
51187                                 const bool exit_on_anykey=false) const {
51188       if (is_empty())
51189         throw CImgInstanceException(_cimg_instance
51190                                     "select_graph(): Empty instance.",
51191                                     cimg_instance);
51192       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
51193                    set_title("CImg<%s>",pixel_type());
51194       const ulongT siz = (ulongT)_width*_height*_depth;
51195       const unsigned int old_normalization = disp.normalization();
51196       disp.show().set_button().set_wheel()._normalization = 0;
51197 
51198       double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
51199       if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
51200       if (nymin==nymax) { --nymin; ++nymax; }
51201       if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; }
51202 
51203       static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
51204       static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
51205 
51206       CImg<ucharT> colormap(3,_spectrum);
51207       if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
51208       else {
51209         colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
51210         if (_spectrum>1) { colormap(0,1) = 10;  colormap(1,1) = 220; colormap(2,1) = 10;  }
51211         if (_spectrum>2) { colormap(0,2) = 10;  colormap(1,2) = 10;  colormap(2,2) = 220; }
51212         if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10;  }
51213         if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10;  colormap(2,4) = 220; }
51214         if (_spectrum>5) { colormap(0,5) = 10;  colormap(1,5) = 220; colormap(2,5) = 220; }
51215         if (_spectrum>6) {
51216           cimg_uint64 rng = 10;
51217           cimg_for_inY(colormap,6,colormap.height()-1,k) {
51218             colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
51219             colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
51220             colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
51221           }
51222         }
51223       }
51224 
51225       CImg<ucharT> visu0, visu, graph, text, axes;
51226       int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
51227       const unsigned int one = plot_type==3?0U:1U;
51228       unsigned int okey = 0, obutton = 0, font_size = 32;
51229       CImg<charT> message(1024);
51230       CImg_3x3(I,unsigned char);
51231 
51232       for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
51233         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
51234         const unsigned int key = disp.key(), button = disp.button();
51235 
51236         // Generate graph representation.
51237         if (!visu0) {
51238           visu0.assign(disp.width(),disp.height(),1,3,220);
51239           const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
51240           if (gdimx>0 && gdimy>0) {
51241             graph.assign(gdimx,gdimy,1,3,255);
51242             if (siz<32) {
51243               if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
51244                                          false,true,black,0.2f,0x33333333,0x33333333);
51245             } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
51246             cimg_forC(*this,c)
51247               graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
51248                                plot_type,vertex_type,nymax,nymin);
51249 
51250             axes.assign(gdimx,gdimy,1,1,0);
51251             const float
51252               dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
51253               px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.),
51254               py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.);
51255             const CImg<Tdouble>
51256               seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
51257                 CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz),
51258               seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin);
51259 
51260             const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
51261             axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py);
51262             if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px);
51263             if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px);
51264             if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py);
51265             if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py);
51266 
51267             cimg_for3x3(axes,x,y,0,0,I,unsigned char)
51268               if (Icc) {
51269                 if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
51270                 else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
51271               }
51272               else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
51273                 cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);
51274 
51275             visu0.draw_image(16,16,graph);
51276             visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
51277               draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
51278           } else graph.assign();
51279           text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
51280           visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
51281           text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
51282           visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
51283           visu.assign();
51284         }
51285 
51286         // Generate and display current view.
51287         if (!visu) {
51288           visu.assign(visu0);
51289           if (graph && x0>=0 && x1>=0) {
51290             const int
51291               nx0 = x0<=x1?x0:x1,
51292               nx1 = x0<=x1?x1:x0,
51293               ny0 = y0<=y1?y0:y1,
51294               ny1 = y0<=y1?y1:y0,
51295               sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
51296               sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
51297               sy0 = 16 + ny0,
51298               sy1 = 16 + ny1;
51299             if (y0>=0 && y1>=0)
51300               visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
51301             else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
51302                    draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
51303                    draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
51304           }
51305           if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
51306             if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
51307             const unsigned int
51308               x = (unsigned int)cimg::round((mouse_x - 16.f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
51309             const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
51310             if (_spectrum>=7)
51311               cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
51312                             (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
51313                             (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
51314                             (double)(*this)(x,0,0,_spectrum - 1));
51315             else {
51316               cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
51317               cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
51318               cimg_sprintf(message._data + std::strlen(message),")");
51319             }
51320             if (x0>=0 && x1>=0) {
51321               const unsigned int
51322                 nx0 = (unsigned int)(x0<=x1?x0:x1),
51323                 nx1 = (unsigned int)(x0<=x1?x1:x0),
51324                 ny0 = (unsigned int)(y0<=y1?y0:y1),
51325                 ny1 = (unsigned int)(y0<=y1?y1:y0);
51326               const double
51327                 cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
51328                 cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
51329                 cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
51330                 cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
51331               if (y0>=0 && y1>=0)
51332                 cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
51333                              x0,cx0,cy0,x1 + one,cx1,cy1);
51334               else
51335                 cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]",
51336                              x0,cx0,x1 + one,cx1);
51337             }
51338             text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
51339             visu.draw_image((visu.width() - text.width())/2,1,~text);
51340           }
51341           visu.display(disp);
51342         }
51343 
51344         // Test keys.
51345         CImg<charT> filename(32);
51346         switch (okey = key) {
51347 #if cimg_OS!=2
51348         case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
51349 #endif
51350         case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
51351         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51352           disp.set_fullscreen(false).
51353             resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
51354                    CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
51355             _is_resized = true;
51356           disp.set_key(key,false); okey = 0;
51357         } break;
51358         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51359           disp.set_fullscreen(false).
51360             resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
51361           disp.set_key(key,false); okey = 0;
51362         } break;
51363         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51364             disp.set_fullscreen(false).
51365               resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
51366                                     CImgDisplay::screen_height()/2,1),false)._is_resized = true;
51367             disp.set_key(key,false); okey = 0;
51368           } break;
51369         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51370             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
51371             disp.set_key(key,false); okey = 0;
51372           } break;
51373         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51374             static unsigned int snap_number = 0;
51375             if (visu || visu0) {
51376               CImg<ucharT> &screen = visu?visu:visu0;
51377               std::FILE *file;
51378               do {
51379                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
51380                 if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
51381               } while (file);
51382               (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
51383               screen.save(filename);
51384               (+screen).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
51385             }
51386             disp.set_key(key,false); okey = 0;
51387           } break;
51388         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51389             static unsigned int snap_number = 0;
51390             if (visu || visu0) {
51391               CImg<ucharT> &screen = visu?visu:visu0;
51392               std::FILE *file;
51393               do {
51394 
51395 #ifdef cimg_use_zlib
51396                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
51397 #else
51398                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
51399 #endif
51400                 if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
51401               } while (file);
51402               (+screen).__draw_text(" Saving instance... ",font_size,0).display(disp);
51403               save(filename);
51404               (+screen).__draw_text(" Instance '%s' saved. ",font_size,0,filename._data).display(disp);
51405             }
51406             disp.set_key(key,false); okey = 0;
51407           } break;
51408         }
51409 
51410         // Handle mouse motion and mouse buttons.
51411         if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
51412           visu.assign();
51413           if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
51414             const int
51415               mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
51416               cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
51417               my = mouse_y - 16,
51418               cy = cimg::cut(my,0,disp.height() - 32);
51419             if (button&1) {
51420               if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
51421             }
51422             else if (button&2) {
51423               if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
51424             }
51425             else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
51426           } else if (!button && obutton) selected = true;
51427           obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
51428         }
51429         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
51430         if (visu && visu0) disp.wait();
51431         if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
51432             (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
51433           disp.set_key(key,false);
51434           okey = 0;
51435         }
51436       }
51437 
51438       disp._normalization = old_normalization;
51439       if (x1>=0 && x1<x0) cimg::swap(x0,x1);
51440       if (y1<y0) cimg::swap(y0,y1);
51441       disp.set_key(okey);
51442       return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);
51443     }
51444 
51445     //! Load image from a file.
51446     /**
51447        \param filename Filename, as a C-string.
51448        \note The extension of \c filename defines the file format. If no filename
51449        extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
51450     **/
51451     CImg<T>& load(const char *const filename) {
51452       if (!filename)
51453         throw CImgArgumentException(_cimg_instance
51454                                     "load(): Specified filename is (null).",
51455                                     cimg_instance);
51456 
51457       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
51458         CImg<charT> filename_local(256);
51459         load(cimg::load_network(filename,filename_local));
51460         std::remove(filename_local);
51461         return *this;
51462       }
51463 
51464       const char *const ext = cimg::split_filename(filename);
51465       const unsigned int omode = cimg::exception_mode();
51466       cimg::exception_mode(0);
51467       bool is_loaded = true;
51468       try {
51469 #ifdef cimg_load_plugin
51470         cimg_load_plugin(filename);
51471 #endif
51472 #ifdef cimg_load_plugin1
51473         cimg_load_plugin1(filename);
51474 #endif
51475 #ifdef cimg_load_plugin2
51476         cimg_load_plugin2(filename);
51477 #endif
51478 #ifdef cimg_load_plugin3
51479         cimg_load_plugin3(filename);
51480 #endif
51481 #ifdef cimg_load_plugin4
51482         cimg_load_plugin4(filename);
51483 #endif
51484 #ifdef cimg_load_plugin5
51485         cimg_load_plugin5(filename);
51486 #endif
51487 #ifdef cimg_load_plugin6
51488         cimg_load_plugin6(filename);
51489 #endif
51490 #ifdef cimg_load_plugin7
51491         cimg_load_plugin7(filename);
51492 #endif
51493 #ifdef cimg_load_plugin8
51494         cimg_load_plugin8(filename);
51495 #endif
51496         // Text formats
51497         if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
51498         else if (!cimg::strcasecmp(ext,"csv") ||
51499                  !cimg::strcasecmp(ext,"dlm") ||
51500                  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
51501         else if (!cimg::strcasecmp(ext,"pdf")) load_pdf_external(filename);
51502 
51503         // 2D binary formats
51504         else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
51505         else if (!cimg::strcasecmp(ext,"jpg") ||
51506                  !cimg::strcasecmp(ext,"jpeg") ||
51507                  !cimg::strcasecmp(ext,"jpe") ||
51508                  !cimg::strcasecmp(ext,"jfif") ||
51509                  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
51510         else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
51511         else if (!cimg::strcasecmp(ext,"ppm") ||
51512                  !cimg::strcasecmp(ext,"pgm") ||
51513                  !cimg::strcasecmp(ext,"pnm") ||
51514                  !cimg::strcasecmp(ext,"pbm") ||
51515                  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
51516         else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
51517         else if (!cimg::strcasecmp(ext,"tif") ||
51518                  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
51519         else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
51520         else if (!cimg::strcasecmp(ext,"cr2") ||
51521                  !cimg::strcasecmp(ext,"crw") ||
51522                  !cimg::strcasecmp(ext,"dcr") ||
51523                  !cimg::strcasecmp(ext,"mrw") ||
51524                  !cimg::strcasecmp(ext,"nef") ||
51525                  !cimg::strcasecmp(ext,"orf") ||
51526                  !cimg::strcasecmp(ext,"pix") ||
51527                  !cimg::strcasecmp(ext,"ptx") ||
51528                  !cimg::strcasecmp(ext,"raf") ||
51529                  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
51530         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
51531         else if (!cimg::strcasecmp(ext,"heic") ||
51532                  !cimg::strcasecmp(ext,"avif")) load_heif(filename);
51533 
51534         // 3D binary formats
51535         else if (!cimg::strcasecmp(ext,"dcm") ||
51536                  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
51537         else if (!cimg::strcasecmp(ext,"hdr") ||
51538                  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
51539         else if (!cimg::strcasecmp(ext,"par") ||
51540                  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
51541         else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
51542         else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
51543         else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
51544         else if (!cimg::strcasecmp(ext,"cimg") ||
51545                  !cimg::strcasecmp(ext,"cimgz") ||
51546                  !*ext)  return load_cimg(filename);
51547 
51548         // Archive files
51549         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
51550 
51551         // Image sequences
51552         else if (!cimg::strcasecmp(ext,"avi") ||
51553                  !cimg::strcasecmp(ext,"mov") ||
51554                  !cimg::strcasecmp(ext,"asf") ||
51555                  !cimg::strcasecmp(ext,"divx") ||
51556                  !cimg::strcasecmp(ext,"flv") ||
51557                  !cimg::strcasecmp(ext,"mpg") ||
51558                  !cimg::strcasecmp(ext,"m1v") ||
51559                  !cimg::strcasecmp(ext,"m2v") ||
51560                  !cimg::strcasecmp(ext,"m4v") ||
51561                  !cimg::strcasecmp(ext,"mjp") ||
51562                  !cimg::strcasecmp(ext,"mp4") ||
51563                  !cimg::strcasecmp(ext,"mkv") ||
51564                  !cimg::strcasecmp(ext,"mpe") ||
51565                  !cimg::strcasecmp(ext,"movie") ||
51566                  !cimg::strcasecmp(ext,"ogm") ||
51567                  !cimg::strcasecmp(ext,"ogg") ||
51568                  !cimg::strcasecmp(ext,"ogv") ||
51569                  !cimg::strcasecmp(ext,"qt") ||
51570                  !cimg::strcasecmp(ext,"rm") ||
51571                  !cimg::strcasecmp(ext,"vob") ||
51572                  !cimg::strcasecmp(ext,"webm") ||
51573                  !cimg::strcasecmp(ext,"wmv") ||
51574                  !cimg::strcasecmp(ext,"xvid") ||
51575                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
51576         else is_loaded = false;
51577       } catch (CImgIOException&) { is_loaded = false; }
51578 
51579       // If nothing loaded, try to guess file format from magic number in file.
51580       if (!is_loaded) {
51581         std::FILE *file = cimg::std_fopen(filename,"rb");
51582         if (!file) {
51583           cimg::exception_mode(omode);
51584           throw CImgIOException(_cimg_instance
51585                                 "load(): Failed to open file '%s'.",
51586                                 cimg_instance,
51587                                 filename);
51588         }
51589 
51590         const char *const f_type = cimg::ftype(file,filename);
51591         cimg::fclose(file);
51592         is_loaded = true;
51593         try {
51594           if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
51595           else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
51596           else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
51597           else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
51598           else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
51599           else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
51600           else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
51601           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
51602           else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
51603           else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
51604           else is_loaded = false;
51605         } catch (CImgIOException&) { is_loaded = false; }
51606       }
51607 
51608       // If nothing loaded, try to load file with other means.
51609       if (!is_loaded) {
51610         try {
51611           load_other(filename);
51612         } catch (CImgIOException&) {
51613           cimg::exception_mode(omode);
51614           throw CImgIOException(_cimg_instance
51615                                 "load(): Failed to recognize format of file '%s'.",
51616                                 cimg_instance,
51617                                 filename);
51618         }
51619       }
51620       cimg::exception_mode(omode);
51621       return *this;
51622     }
51623 
51624     //! Load image from a file \newinstance.
51625     static CImg<T> get_load(const char *const filename) {
51626       return CImg<T>().load(filename);
51627     }
51628 
51629     //! Load image from an ascii file.
51630     /**
51631        \param filename Filename, as a C -string.
51632     **/
51633     CImg<T>& load_ascii(const char *const filename) {
51634       return _load_ascii(0,filename);
51635     }
51636 
51637     //! Load image from an ascii file \inplace.
51638     static CImg<T> get_load_ascii(const char *const filename) {
51639       return CImg<T>().load_ascii(filename);
51640     }
51641 
51642     //! Load image from an ascii file \overloading.
51643     CImg<T>& load_ascii(std::FILE *const file) {
51644       return _load_ascii(file,0);
51645     }
51646 
51647     //! Loadimage from an ascii file \newinstance.
51648     static CImg<T> get_load_ascii(std::FILE *const file) {
51649       return CImg<T>().load_ascii(file);
51650     }
51651 
51652     CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
51653       if (!file && !filename)
51654         throw CImgArgumentException(_cimg_instance
51655                                     "load_ascii(): Specified filename is (null).",
51656                                     cimg_instance);
51657 
51658       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
51659       CImg<charT> line(256); *line = 0;
51660       int err = std::fscanf(nfile,"%255[^\n]",line._data);
51661       unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
51662       cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
51663       err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]");
51664       if (!dx || !dy || !dz || !dc) {
51665         if (!file) cimg::fclose(nfile);
51666         throw CImgIOException(_cimg_instance
51667                               "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
51668                               "to (%u,%u,%u,%u).",
51669                               cimg_instance,
51670                               filename?filename:"(FILE*)",dx,dy,dz,dc);
51671       }
51672       assign(dx,dy,dz,dc);
51673       const ulongT siz = size();
51674       ulongT off = 0;
51675       double val;
51676       T *ptr = _data;
51677       for (err = 1, off = 0; off<siz && err==1; ++off) {
51678         err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val);
51679         *(ptr++) = (T)val;
51680       }
51681       if (err!=1)
51682         cimg::warn(_cimg_instance
51683                    "load_ascii(): Only %lu/%lu values read from file '%s'.",
51684                    cimg_instance,
51685                    off - 1,siz,filename?filename:"(FILE*)");
51686 
51687       if (!file) cimg::fclose(nfile);
51688       return *this;
51689     }
51690 
51691     //! Load image from a DLM file.
51692     /**
51693       \param filename Filename, as a C-string.
51694     **/
51695     CImg<T>& load_dlm(const char *const filename) {
51696       return _load_dlm(0,filename);
51697     }
51698 
51699     //! Load image from a DLM file \newinstance.
51700     static CImg<T> get_load_dlm(const char *const filename) {
51701       return CImg<T>().load_dlm(filename);
51702     }
51703 
51704     //! Load image from a DLM file \overloading.
51705     CImg<T>& load_dlm(std::FILE *const file) {
51706       return _load_dlm(file,0);
51707     }
51708 
51709     //! Load image from a DLM file \newinstance.
51710     static CImg<T> get_load_dlm(std::FILE *const file) {
51711       return CImg<T>().load_dlm(file);
51712     }
51713 
51714     CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
51715       if (!file && !filename)
51716         throw CImgArgumentException(_cimg_instance
51717                                     "load_dlm(): Specified filename is (null).",
51718                                     cimg_instance);
51719 
51720       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
51721       CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0;
51722       unsigned int cdx = 0, dx = 0, dy = 0;
51723       int err = 0;
51724       double val;
51725       assign(256,256,1,1,(T)0);
51726       while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) {
51727         if (err>0) (*this)(cdx++,dy) = (T)val;
51728         if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
51729         char c = 0;
51730         if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') {
51731           dx = std::max(cdx,dx);
51732           if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
51733           cdx = 0;
51734         }
51735       }
51736       if (cdx && err==1) { dx = cdx; ++dy; }
51737       if (!dx || !dy) {
51738         if (!file) cimg::fclose(nfile);
51739         throw CImgIOException(_cimg_instance
51740                               "load_dlm(): Invalid DLM file '%s'.",
51741                               cimg_instance,
51742                               filename?filename:"(FILE*)");
51743       }
51744       resize(dx,dy,1,1,0);
51745       if (!file) cimg::fclose(nfile);
51746       return *this;
51747     }
51748 
51749     //! Load image from a BMP file.
51750     /**
51751        \param filename Filename, as a C-string.
51752     **/
51753     CImg<T>& load_bmp(const char *const filename) {
51754       return _load_bmp(0,filename);
51755     }
51756 
51757     //! Load image from a BMP file \newinstance.
51758     static CImg<T> get_load_bmp(const char *const filename) {
51759       return CImg<T>().load_bmp(filename);
51760     }
51761 
51762     //! Load image from a BMP file \overloading.
51763     CImg<T>& load_bmp(std::FILE *const file) {
51764       return _load_bmp(file,0);
51765     }
51766 
51767     //! Load image from a BMP file \newinstance.
51768     static CImg<T> get_load_bmp(std::FILE *const file) {
51769       return CImg<T>().load_bmp(file);
51770     }
51771 
51772     CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
51773       if (!file && !filename)
51774         throw CImgArgumentException(_cimg_instance
51775                                     "load_bmp(): Specified filename is (null).",
51776                                     cimg_instance);
51777 
51778       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
51779       CImg<ucharT> header(54);
51780       cimg::fread(header._data,54,nfile);
51781       if (*header!='B' || header[1]!='M') {
51782         if (!file) cimg::fclose(nfile);
51783         throw CImgIOException(_cimg_instance
51784                               "load_bmp(): Invalid BMP file '%s'.",
51785                               cimg_instance,
51786                               filename?filename:"(FILE*)");
51787       }
51788 
51789       // Read header and pixel buffer
51790       int
51791         file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
51792         offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
51793         header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
51794         dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
51795         dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
51796         compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
51797         nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
51798         bpp = header[0x1C] + (header[0x1D]<<8);
51799 
51800       if (!file_size || file_size==offset) {
51801         cimg::fseek(nfile,0,SEEK_END);
51802         file_size = (int)cimg::ftell(nfile);
51803         cimg::fseek(nfile,54,SEEK_SET);
51804       }
51805       if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR);
51806 
51807       const int
51808         dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)),
51809         align_bytes = (4 - dx_bytes%4)%4;
51810       const ulongT
51811         cimg_iobuffer = (ulongT)24*1024*1024,
51812         buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes);
51813 
51814       CImg<intT> colormap;
51815       if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
51816       if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
51817       const int xoffset = offset - 14 - header_size - 4*nb_colors;
51818       if (xoffset>0) cimg::fseek(nfile,xoffset,SEEK_CUR);
51819 
51820       CImg<ucharT> buffer;
51821       if (buf_size<cimg_iobuffer) {
51822         buffer.assign(buf_size,1,1,1,0);
51823         cimg::fread(buffer._data,buf_size,nfile);
51824       } else buffer.assign(dx_bytes + align_bytes);
51825       unsigned char *ptrs = buffer;
51826 
51827       // Decompress buffer (if necessary)
51828       if (compression==1 || compression==2) {
51829         if (file)
51830           throw CImgIOException(_cimg_instance
51831                                 "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
51832                                 cimg_instance);
51833         else {
51834           if (!file) cimg::fclose(nfile);
51835           return load_other(filename);
51836         }
51837       }
51838 
51839       // Read pixel data
51840       assign(dx,cimg::abs(dy),1,3,0);
51841       switch (bpp) {
51842       case 1 : { // Monochrome
51843         if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) {
51844           if (buf_size>=cimg_iobuffer) {
51845             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
51846             cimg::fseek(nfile,align_bytes,SEEK_CUR);
51847           }
51848           unsigned char mask = 0x80, val = 0;
51849           cimg_forX(*this,x) {
51850             if (mask==0x80) val = *(ptrs++);
51851             const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
51852             (*this)(x,y,2) = (T)*(col++);
51853             (*this)(x,y,1) = (T)*(col++);
51854             (*this)(x,y,0) = (T)*(col++);
51855             mask = cimg::ror(mask);
51856           }
51857           ptrs+=align_bytes;
51858         }
51859       } break;
51860       case 4 : { // 16 colors
51861         if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) {
51862           if (buf_size>=cimg_iobuffer) {
51863             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
51864             cimg::fseek(nfile,align_bytes,SEEK_CUR);
51865           }
51866           unsigned char mask = 0xF0, val = 0;
51867           cimg_forX(*this,x) {
51868             if (mask==0xF0) val = *(ptrs++);
51869             const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
51870             const unsigned char *col = (unsigned char*)(colormap._data + color);
51871             (*this)(x,y,2) = (T)*(col++);
51872             (*this)(x,y,1) = (T)*(col++);
51873             (*this)(x,y,0) = (T)*(col++);
51874             mask = cimg::ror(mask,4);
51875           }
51876           ptrs+=align_bytes;
51877         }
51878       } break;
51879       case 8 : { // 256 colors
51880         if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) {
51881           if (buf_size>=cimg_iobuffer) {
51882             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
51883             cimg::fseek(nfile,align_bytes,SEEK_CUR);
51884           }
51885           cimg_forX(*this,x) {
51886             const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
51887             (*this)(x,y,2) = (T)*(col++);
51888             (*this)(x,y,1) = (T)*(col++);
51889             (*this)(x,y,0) = (T)*(col++);
51890           }
51891           ptrs+=align_bytes;
51892         }
51893       } break;
51894       case 16 : { // 16 bits colors (RGB565)
51895         for (int y = height() - 1; y>=0; --y) {
51896           if (buf_size>=cimg_iobuffer) {
51897             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
51898             cimg::fseek(nfile,align_bytes,SEEK_CUR);
51899           }
51900           cimg_forX(*this,x) {
51901             const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
51902             const unsigned short col = (unsigned short)c2<<8 | c1;
51903             (*this)(x,y,2) = (T)((col&0x1F)<<3);
51904             (*this)(x,y,1) = (T)(((col>>5)&0x3F)<<3);
51905             (*this)(x,y,0) = (T)(((col>>11)&0x1F)<<3);
51906           }
51907           ptrs+=align_bytes;
51908         }
51909       } break;
51910       case 24 : { // 24 bits colors
51911         for (int y = height() - 1; y>=0; --y) {
51912           if (buf_size>=cimg_iobuffer) {
51913             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
51914             cimg::fseek(nfile,align_bytes,SEEK_CUR);
51915           }
51916           cimg_forX(*this,x) {
51917             (*this)(x,y,2) = (T)*(ptrs++);
51918             (*this)(x,y,1) = (T)*(ptrs++);
51919             (*this)(x,y,0) = (T)*(ptrs++);
51920           }
51921           ptrs+=align_bytes;
51922         }
51923       } break;
51924       case 32 : { // 32 bits colors
51925         for (int y = height() - 1; y>=0; --y) {
51926           if (buf_size>=cimg_iobuffer) {
51927             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
51928             cimg::fseek(nfile,align_bytes,SEEK_CUR);
51929           }
51930           cimg_forX(*this,x) {
51931             (*this)(x,y,2) = (T)*(ptrs++);
51932             (*this)(x,y,1) = (T)*(ptrs++);
51933             (*this)(x,y,0) = (T)*(ptrs++);
51934             ++ptrs;
51935           }
51936           ptrs+=align_bytes;
51937         }
51938       } break;
51939       }
51940       if (dy<0) mirror('y');
51941       if (!file) cimg::fclose(nfile);
51942       return *this;
51943     }
51944 
51945     //! Load image from a JPEG file.
51946     /**
51947        \param filename Filename, as a C-string.
51948     **/
51949     CImg<T>& load_jpeg(const char *const filename) {
51950       return _load_jpeg(0,filename);
51951     }
51952 
51953     //! Load image from a JPEG file \newinstance.
51954     static CImg<T> get_load_jpeg(const char *const filename) {
51955       return CImg<T>().load_jpeg(filename);
51956     }
51957 
51958     //! Load image from a JPEG file \overloading.
51959     CImg<T>& load_jpeg(std::FILE *const file) {
51960       return _load_jpeg(file,0);
51961     }
51962 
51963     //! Load image from a JPEG file \newinstance.
51964     static CImg<T> get_load_jpeg(std::FILE *const file) {
51965       return CImg<T>().load_jpeg(file);
51966     }
51967 
51968     // Custom error handler for libjpeg.
51969 #ifdef cimg_use_jpeg
51970     struct _cimg_error_mgr {
51971       struct jpeg_error_mgr original;
51972       jmp_buf setjmp_buffer;
51973       char message[JMSG_LENGTH_MAX];
51974     };
51975 
51976     typedef struct _cimg_error_mgr *_cimg_error_ptr;
51977 
51978     METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
51979       _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err;  // Return control to the setjmp point
51980       (*cinfo->err->format_message)(cinfo,c_err->message);
51981       jpeg_destroy(cinfo);  // Clean memory and temp files
51982       longjmp(c_err->setjmp_buffer,1);
51983     }
51984 #endif
51985 
51986     CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
51987       if (!file && !filename)
51988         throw CImgArgumentException(_cimg_instance
51989                                     "load_jpeg(): Specified filename is (null).",
51990                                     cimg_instance);
51991 
51992 #ifndef cimg_use_jpeg
51993       if (file)
51994         throw CImgIOException(_cimg_instance
51995                               "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
51996                               cimg_instance);
51997       else return load_other(filename);
51998 #else
51999 
52000       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52001       struct jpeg_decompress_struct cinfo;
52002       struct _cimg_error_mgr jerr;
52003       cinfo.err = jpeg_std_error(&jerr.original);
52004       jerr.original.error_exit = _cimg_jpeg_error_exit;
52005       if (setjmp(jerr.setjmp_buffer)) { // JPEG error
52006         if (!file) cimg::fclose(nfile);
52007         throw CImgIOException(_cimg_instance
52008                              "load_jpeg(): Error message returned by libjpeg: %s.",
52009                              cimg_instance,jerr.message);
52010       }
52011 
52012       jpeg_create_decompress(&cinfo);
52013       jpeg_stdio_src(&cinfo,nfile);
52014       jpeg_read_header(&cinfo,TRUE);
52015       jpeg_start_decompress(&cinfo);
52016 
52017       if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
52018         if (!file) {
52019           cimg::fclose(nfile);
52020           return load_other(filename);
52021         } else
52022           throw CImgIOException(_cimg_instance
52023                                 "load_jpeg(): Failed to load JPEG data from file '%s'.",
52024                                 cimg_instance,filename?filename:"(FILE*)");
52025       }
52026       CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
52027       JSAMPROW row_pointer[1];
52028       try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); }
52029       catch (...) { if (!file) cimg::fclose(nfile); throw; }
52030       T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
52031         *ptr_a = _data + 3UL*_width*_height;
52032       while (cinfo.output_scanline<cinfo.output_height) {
52033         *row_pointer = buffer._data;
52034         if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
52035           cimg::warn(_cimg_instance
52036                      "load_jpeg(): Incomplete data in file '%s'.",
52037                      cimg_instance,filename?filename:"(FILE*)");
52038           break;
52039         }
52040         const unsigned char *ptrs = buffer._data;
52041         switch (_spectrum) {
52042         case 1 : {
52043           cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
52044         } break;
52045         case 3 : {
52046           cimg_forX(*this,x) {
52047             *(ptr_r++) = (T)*(ptrs++);
52048             *(ptr_g++) = (T)*(ptrs++);
52049             *(ptr_b++) = (T)*(ptrs++);
52050           }
52051         } break;
52052         case 4 : {
52053           cimg_forX(*this,x) {
52054             *(ptr_r++) = (T)*(ptrs++);
52055             *(ptr_g++) = (T)*(ptrs++);
52056             *(ptr_b++) = (T)*(ptrs++);
52057             *(ptr_a++) = (T)*(ptrs++);
52058           }
52059         } break;
52060         }
52061       }
52062       jpeg_finish_decompress(&cinfo);
52063       jpeg_destroy_decompress(&cinfo);
52064       if (!file) cimg::fclose(nfile);
52065       return *this;
52066 #endif
52067     }
52068 
52069     //! Load image from a file, using Magick++ library.
52070     /**
52071        \param filename Filename, as a C-string.
52072     **/
52073     // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
52074     //   This is experimental code, not much tested, use with care.
52075     CImg<T>& load_magick(const char *const filename) {
52076       if (!filename)
52077         throw CImgArgumentException(_cimg_instance
52078                                     "load_magick(): Specified filename is (null).",
52079                                     cimg_instance);
52080 
52081 #ifdef cimg_use_magick
52082       Magick::Image image(filename);
52083       const unsigned int W = image.size().width(), H = image.size().height();
52084       switch (image.type()) {
52085       case Magick::PaletteMatteType :
52086       case Magick::TrueColorMatteType :
52087       case Magick::ColorSeparationType : {
52088         assign(W,H,1,4);
52089         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);
52090         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
52091         for (ulongT off = (ulongT)W*H; off; --off) {
52092           *(ptr_r++) = (T)(pixels->red);
52093           *(ptr_g++) = (T)(pixels->green);
52094           *(ptr_b++) = (T)(pixels->blue);
52095           *(ptr_a++) = (T)(pixels->opacity);
52096           ++pixels;
52097         }
52098       } break;
52099       case Magick::PaletteType :
52100       case Magick::TrueColorType : {
52101         assign(W,H,1,3);
52102         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
52103         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
52104         for (ulongT off = (ulongT)W*H; off; --off) {
52105           *(ptr_r++) = (T)(pixels->red);
52106           *(ptr_g++) = (T)(pixels->green);
52107           *(ptr_b++) = (T)(pixels->blue);
52108           ++pixels;
52109         }
52110       } break;
52111       case Magick::GrayscaleMatteType : {
52112         assign(W,H,1,2);
52113         T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
52114         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
52115         for (ulongT off = (ulongT)W*H; off; --off) {
52116           *(ptr_r++) = (T)(pixels->red);
52117           *(ptr_a++) = (T)(pixels->opacity);
52118           ++pixels;
52119         }
52120       } break;
52121       default : {
52122         assign(W,H,1,1);
52123         T *ptr_r = data(0,0,0,0);
52124         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
52125         for (ulongT off = (ulongT)W*H; off; --off) {
52126           *(ptr_r++) = (T)(pixels->red);
52127           ++pixels;
52128         }
52129       }
52130       }
52131       return *this;
52132 #else
52133       throw CImgIOException(_cimg_instance
52134                             "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
52135                             cimg_instance,
52136                             filename);
52137 #endif
52138     }
52139 
52140     //! Load image from a file, using Magick++ library \newinstance.
52141     static CImg<T> get_load_magick(const char *const filename) {
52142       return CImg<T>().load_magick(filename);
52143     }
52144 
52145     //! Load image from a PNG file.
52146     /**
52147        \param filename Filename, as a C-string.
52148        \param[out] bits_per_pixel Number of bits per pixels used to store pixel values in the image file.
52149     **/
52150     CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_pixel=0) {
52151       return _load_png(0,filename,bits_per_pixel);
52152     }
52153 
52154     //! Load image from a PNG file \newinstance.
52155     static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_pixel=0) {
52156       return CImg<T>().load_png(filename,bits_per_pixel);
52157     }
52158 
52159     //! Load image from a PNG file \overloading.
52160     CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) {
52161       return _load_png(file,0,bits_per_pixel);
52162     }
52163 
52164     //! Load image from a PNG file \newinstance.
52165     static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) {
52166       return CImg<T>().load_png(file,bits_per_pixel);
52167     }
52168 
52169     // (Note: Most of this function has been written by Eric Fausett)
52170     CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_pixel) {
52171       if (!file && !filename)
52172         throw CImgArgumentException(_cimg_instance
52173                                     "load_png(): Specified filename is (null).",
52174                                     cimg_instance);
52175 
52176 #ifndef cimg_use_png
52177       cimg::unused(bits_per_pixel);
52178       if (file)
52179         throw CImgIOException(_cimg_instance
52180                               "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
52181                               cimg_instance);
52182 
52183       else return load_other(filename);
52184 #else
52185       // Open file and check for PNG validity
52186 #if defined __GNUC__
52187       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
52188       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
52189 #else
52190       const char *nfilename = filename;
52191       std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb");
52192 #endif
52193       unsigned char pngCheck[8] = { 0 };
52194       cimg::fread(pngCheck,8,(std::FILE*)nfile);
52195       if (png_sig_cmp(pngCheck,0,8)) {
52196         if (!file) cimg::fclose(nfile);
52197         throw CImgIOException(_cimg_instance
52198                               "load_png(): Invalid PNG file '%s'.",
52199                               cimg_instance,
52200                               nfilename?nfilename:"(FILE*)");
52201       }
52202 
52203       // Setup PNG structures for read
52204       png_voidp user_error_ptr = 0;
52205       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
52206       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
52207       if (!png_ptr) {
52208         if (!file) cimg::fclose(nfile);
52209         throw CImgIOException(_cimg_instance
52210                               "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
52211                               cimg_instance,
52212                               nfilename?nfilename:"(FILE*)");
52213       }
52214       png_infop info_ptr = png_create_info_struct(png_ptr);
52215       if (!info_ptr) {
52216         if (!file) cimg::fclose(nfile);
52217         png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
52218         throw CImgIOException(_cimg_instance
52219                               "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
52220                               cimg_instance,
52221                               nfilename?nfilename:"(FILE*)");
52222       }
52223       png_infop end_info = png_create_info_struct(png_ptr);
52224       if (!end_info) {
52225         if (!file) cimg::fclose(nfile);
52226         png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
52227         throw CImgIOException(_cimg_instance
52228                               "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
52229                               cimg_instance,
52230                               nfilename?nfilename:"(FILE*)");
52231       }
52232 
52233       // Error handling callback for png file reading
52234       if (setjmp(png_jmpbuf(png_ptr))) {
52235         if (!file) cimg::fclose((std::FILE*)nfile);
52236         png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
52237         throw CImgIOException(_cimg_instance
52238                               "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
52239                               cimg_instance,
52240                               nfilename?nfilename:"(FILE*)");
52241       }
52242       png_init_io(png_ptr, nfile);
52243       png_set_sig_bytes(png_ptr, 8);
52244 
52245       // Get PNG Header Info up to data block
52246       png_read_info(png_ptr,info_ptr);
52247       png_uint_32 W, H;
52248       int bit_depth, color_type, interlace_type;
52249       bool is_gray = false;
52250       png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
52251       if (bits_per_pixel) *bits_per_pixel = (unsigned int)bit_depth;
52252 
52253       // Transforms to unify image data
52254       if (color_type==PNG_COLOR_TYPE_PALETTE) {
52255         png_set_palette_to_rgb(png_ptr);
52256         color_type = PNG_COLOR_TYPE_RGB;
52257         bit_depth = 8;
52258       }
52259       if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
52260         png_set_expand_gray_1_2_4_to_8(png_ptr);
52261         is_gray = true;
52262         bit_depth = 8;
52263       }
52264       if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
52265         png_set_tRNS_to_alpha(png_ptr);
52266         color_type |= PNG_COLOR_MASK_ALPHA;
52267       }
52268       if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
52269         png_set_gray_to_rgb(png_ptr);
52270         color_type |= PNG_COLOR_MASK_COLOR;
52271         is_gray = true;
52272       }
52273       if (color_type==PNG_COLOR_TYPE_RGB)
52274         png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
52275 
52276       png_read_update_info(png_ptr,info_ptr);
52277       if (bit_depth!=8 && bit_depth!=16) {
52278         if (!file) cimg::fclose(nfile);
52279         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
52280         throw CImgIOException(_cimg_instance
52281                               "load_png(): Invalid bit depth %u in file '%s'.",
52282                               cimg_instance,
52283                               bit_depth,nfilename?nfilename:"(FILE*)");
52284       }
52285       const int byte_depth = bit_depth>>3;
52286 
52287       // Allocate memory for image reading
52288       png_bytep *const imgData = new png_bytep[H];
52289       for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W];
52290       png_read_image(png_ptr,imgData);
52291       png_read_end(png_ptr,end_info);
52292 
52293       // Read pixel data
52294       if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
52295         if (!file) cimg::fclose(nfile);
52296         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
52297         throw CImgIOException(_cimg_instance
52298                               "load_png(): Invalid color coding type %u in file '%s'.",
52299                               cimg_instance,
52300                               color_type,nfilename?nfilename:"(FILE*)");
52301       }
52302       const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
52303       try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); }
52304       catch (...) { if (!file) cimg::fclose(nfile); throw; }
52305       T
52306         *ptr_r = data(0,0,0,0),
52307         *ptr_g = is_gray?0:data(0,0,0,1),
52308         *ptr_b = is_gray?0:data(0,0,0,2),
52309         *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
52310       switch (bit_depth) {
52311       case 8 : {
52312         cimg_forY(*this,y) {
52313           const unsigned char *ptrs = (unsigned char*)imgData[y];
52314           cimg_forX(*this,x) {
52315             *(ptr_r++) = (T)*(ptrs++);
52316             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
52317             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
52318             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
52319           }
52320         }
52321       } break;
52322       case 16 : {
52323         cimg_forY(*this,y) {
52324           const unsigned short *ptrs = (unsigned short*)(imgData[y]);
52325           if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
52326           cimg_forX(*this,x) {
52327             *(ptr_r++) = (T)*(ptrs++);
52328             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
52329             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
52330             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
52331           }
52332         }
52333       } break;
52334       }
52335       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
52336 
52337       // Deallocate image read memory
52338       cimg_forY(*this,n) delete[] imgData[n];
52339       delete[] imgData;
52340       if (!file) cimg::fclose(nfile);
52341       return *this;
52342 #endif
52343     }
52344 
52345     //! Load image from a PNM file.
52346     /**
52347       \param filename Filename, as a C-string.
52348     **/
52349     CImg<T>& load_pnm(const char *const filename) {
52350       return _load_pnm(0,filename);
52351     }
52352 
52353     //! Load image from a PNM file \newinstance.
52354     static CImg<T> get_load_pnm(const char *const filename) {
52355       return CImg<T>().load_pnm(filename);
52356     }
52357 
52358     //! Load image from a PNM file \overloading.
52359     CImg<T>& load_pnm(std::FILE *const file) {
52360       return _load_pnm(file,0);
52361     }
52362 
52363     //! Load image from a PNM file \newinstance.
52364     static CImg<T> get_load_pnm(std::FILE *const file) {
52365       return CImg<T>().load_pnm(file);
52366     }
52367 
52368     CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
52369       if (!file && !filename)
52370         throw CImgArgumentException(_cimg_instance
52371                                     "load_pnm(): Specified filename is (null).",
52372                                     cimg_instance);
52373 
52374       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52375       unsigned int ppm_type, W, H, D = 1, colormax = 255;
52376       CImg<charT> item(16384,1,1,1,0);
52377       int err, rval, gval, bval;
52378       const longT cimg_iobuffer = (longT)24*1024*1024;
52379       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52380       if (cimg_sscanf(item," P%u",&ppm_type)!=1) {
52381         if (!file) cimg::fclose(nfile);
52382         throw CImgIOException(_cimg_instance
52383                               "load_pnm(): PNM header not found in file '%s'.",
52384                               cimg_instance,
52385                               filename?filename:"(FILE*)");
52386       }
52387       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52388       if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
52389         if (!file) cimg::fclose(nfile);
52390         throw CImgIOException(_cimg_instance
52391                               "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
52392                               cimg_instance,
52393                               filename?filename:"(FILE*)");
52394       }
52395       if (ppm_type!=1 && ppm_type!=4) {
52396         if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
52397           while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52398           if (cimg_sscanf(item,"%u",&colormax)!=1)
52399             cimg::warn(_cimg_instance
52400                        "load_pnm(): COLORMAX field is undefined in file '%s'.",
52401                        cimg_instance,
52402                        filename?filename:"(FILE*)");
52403         } else { colormax = D; D = 1; }
52404       }
52405       std::fgetc(nfile);
52406 
52407       if (filename) { // Check that dimensions specified in file does not exceed the buffer dimension
52408         const cimg_int64 siz = cimg::fsize(filename);
52409         if (W*H*D>siz)
52410           throw CImgIOException(_cimg_instance
52411                                 "load_pnm(): Specified image dimensions in file '%s' exceed file size.",
52412                                 cimg_instance,
52413                                 filename);
52414       }
52415 
52416       switch (ppm_type) {
52417       case 1 : { // 2D B&W ascii
52418         assign(W,H,1,1);
52419         T* ptrd = _data;
52420         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
52421       } break;
52422       case 2 : { // 2D grey ascii
52423         assign(W,H,1,1);
52424         T* ptrd = _data;
52425         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
52426       } break;
52427       case 3 : { // 2D color ascii
52428         assign(W,H,1,3);
52429         T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
52430         cimg_forXY(*this,x,y) {
52431           if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
52432             *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
52433           } else break;
52434         }
52435       } break;
52436       case 4 : { // 2D b&w binary (support 3D PINK extension)
52437         CImg<ucharT> raw;
52438         assign(W,H,D,1);
52439         T *ptrd = data(0,0,0,0);
52440         unsigned int w = 0, h = 0, d = 0;
52441         for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
52442           raw.assign(std::min(to_read,cimg_iobuffer));
52443           cimg::fread(raw._data,raw._width,nfile);
52444           to_read-=raw._width;
52445           const unsigned char *ptrs = raw._data;
52446           unsigned char mask = 0, val = 0;
52447           for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) {
52448             if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
52449             *(ptrd++) = (T)((val&mask)?0:255);
52450             if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
52451           }
52452         }
52453       } break;
52454       case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension)
52455         if (colormax<256) { // 8 bits
52456           CImg<ucharT> raw;
52457           assign(W,H,D,1);
52458           T *ptrd = data(0,0,0,0);
52459           for (longT to_read = (longT)size(); to_read>0; ) {
52460             raw.assign(std::min(to_read,cimg_iobuffer));
52461             cimg::fread(raw._data,raw._width,nfile);
52462             to_read-=raw._width;
52463             const unsigned char *ptrs = raw._data;
52464             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
52465           }
52466         } else { // 16 bits
52467           CImg<ushortT> raw;
52468           assign(W,H,D,1);
52469           T *ptrd = data(0,0,0,0);
52470           for (longT to_read = (longT)size(); to_read>0; ) {
52471             raw.assign(std::min(to_read,cimg_iobuffer/2));
52472             cimg::fread(raw._data,raw._width,nfile);
52473             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
52474             to_read-=raw._width;
52475             const unsigned short *ptrs = raw._data;
52476             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
52477           }
52478         }
52479       } break;
52480       case 6 : { // 2D color binary
52481         if (colormax<256) { // 8 bits
52482           CImg<ucharT> raw;
52483           assign(W,H,1,3);
52484           T
52485             *ptr_r = data(0,0,0,0),
52486             *ptr_g = data(0,0,0,1),
52487             *ptr_b = data(0,0,0,2);
52488           for (longT to_read = (longT)size(); to_read>0; ) {
52489             raw.assign(std::min(to_read,cimg_iobuffer));
52490             cimg::fread(raw._data,raw._width,nfile);
52491             to_read-=raw._width;
52492             const unsigned char *ptrs = raw._data;
52493             for (ulongT off = (ulongT)raw._width/3; off; --off) {
52494               *(ptr_r++) = (T)*(ptrs++);
52495               *(ptr_g++) = (T)*(ptrs++);
52496               *(ptr_b++) = (T)*(ptrs++);
52497             }
52498           }
52499         } else { // 16 bits
52500           CImg<ushortT> raw;
52501           assign(W,H,1,3);
52502           T
52503             *ptr_r = data(0,0,0,0),
52504             *ptr_g = data(0,0,0,1),
52505             *ptr_b = data(0,0,0,2);
52506           for (longT to_read = (longT)size(); to_read>0; ) {
52507             raw.assign(std::min(to_read,cimg_iobuffer/2));
52508             cimg::fread(raw._data,raw._width,nfile);
52509             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
52510             to_read-=raw._width;
52511             const unsigned short *ptrs = raw._data;
52512             for (ulongT off = (ulongT)raw._width/3; off; --off) {
52513               *(ptr_r++) = (T)*(ptrs++);
52514               *(ptr_g++) = (T)*(ptrs++);
52515               *(ptr_b++) = (T)*(ptrs++);
52516             }
52517           }
52518         }
52519       } break;
52520       case 8 : { // 2D/3D grey binary with int32 integers (PINK extension)
52521         CImg<intT> raw;
52522         assign(W,H,D,1);
52523         T *ptrd = data(0,0,0,0);
52524         for (longT to_read = (longT)size(); to_read>0; ) {
52525           raw.assign(std::min(to_read,cimg_iobuffer));
52526           cimg::fread(raw._data,raw._width,nfile);
52527           to_read-=raw._width;
52528           const int *ptrs = raw._data;
52529           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
52530         }
52531       } break;
52532       case 9 : { // 2D/3D grey binary with float values (PINK extension)
52533         CImg<floatT> raw;
52534         assign(W,H,D,1);
52535         T *ptrd = data(0,0,0,0);
52536         for (longT to_read = (longT)size(); to_read>0; ) {
52537           raw.assign(std::min(to_read,cimg_iobuffer));
52538           cimg::fread(raw._data,raw._width,nfile);
52539           to_read-=raw._width;
52540           const float *ptrs = raw._data;
52541           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
52542         }
52543       } break;
52544       default :
52545         assign();
52546         if (!file) cimg::fclose(nfile);
52547         throw CImgIOException(_cimg_instance
52548                               "load_pnm(): PNM type 'P%d' found, but type is not supported.",
52549                               cimg_instance,
52550                               filename?filename:"(FILE*)",ppm_type);
52551       }
52552       if (!file) cimg::fclose(nfile);
52553       return *this;
52554     }
52555 
52556     //! Load image from a PFM file.
52557     /**
52558       \param filename Filename, as a C-string.
52559     **/
52560     CImg<T>& load_pfm(const char *const filename) {
52561       return _load_pfm(0,filename);
52562     }
52563 
52564     //! Load image from a PFM file \newinstance.
52565     static CImg<T> get_load_pfm(const char *const filename) {
52566       return CImg<T>().load_pfm(filename);
52567     }
52568 
52569     //! Load image from a PFM file \overloading.
52570     CImg<T>& load_pfm(std::FILE *const file) {
52571       return _load_pfm(file,0);
52572     }
52573 
52574     //! Load image from a PFM file \newinstance.
52575     static CImg<T> get_load_pfm(std::FILE *const file) {
52576       return CImg<T>().load_pfm(file);
52577     }
52578 
52579     CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
52580       if (!file && !filename)
52581         throw CImgArgumentException(_cimg_instance
52582                                     "load_pfm(): Specified filename is (null).",
52583                                     cimg_instance);
52584 
52585       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52586       char pfm_type;
52587       CImg<charT> item(16384,1,1,1,0);
52588       int W = 0, H = 0, err = 0;
52589       double scale = 0;
52590       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52591       if (cimg_sscanf(item," P%c",&pfm_type)!=1) {
52592         if (!file) cimg::fclose(nfile);
52593         throw CImgIOException(_cimg_instance
52594                               "load_pfm(): PFM header not found in file '%s'.",
52595                               cimg_instance,
52596                               filename?filename:"(FILE*)");
52597       }
52598       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52599       if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) {
52600         if (!file) cimg::fclose(nfile);
52601         throw CImgIOException(_cimg_instance
52602                               "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
52603                               cimg_instance,
52604                               filename?filename:"(FILE*)");
52605       } else if (W<=0 || H<=0) {
52606         if (!file) cimg::fclose(nfile);
52607         throw CImgIOException(_cimg_instance
52608                               "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.",
52609                               cimg_instance,W,H,
52610                               filename?filename:"(FILE*)");
52611       }
52612       if (err==2) {
52613         while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
52614         if (cimg_sscanf(item,"%lf",&scale)!=1)
52615           cimg::warn(_cimg_instance
52616                      "load_pfm(): SCALE field is undefined in file '%s'.",
52617                      cimg_instance,
52618                      filename?filename:"(FILE*)");
52619       }
52620       std::fgetc(nfile);
52621       const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
52622       if (is_color) {
52623         assign(W,H,1,3,(T)0);
52624         CImg<floatT> buf(3*W);
52625         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
52626         cimg_forY(*this,y) {
52627           cimg::fread(buf._data,3*W,nfile);
52628           if (is_inverted) cimg::invert_endianness(buf._data,3*W);
52629           const float *ptrs = buf._data;
52630           cimg_forX(*this,x) {
52631             *(ptr_r++) = (T)*(ptrs++);
52632             *(ptr_g++) = (T)*(ptrs++);
52633             *(ptr_b++) = (T)*(ptrs++);
52634           }
52635         }
52636       } else {
52637         assign(W,H,1,1,(T)0);
52638         CImg<floatT> buf(W);
52639         T *ptrd = data(0,0,0,0);
52640         cimg_forY(*this,y) {
52641           cimg::fread(buf._data,W,nfile);
52642           if (is_inverted) cimg::invert_endianness(buf._data,W);
52643           const float *ptrs = buf._data;
52644           cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
52645         }
52646       }
52647       if (!file) cimg::fclose(nfile);
52648       return mirror('y');  // Most of the .pfm files are flipped along the y-axis
52649     }
52650 
52651     //! Load image from a RGB file.
52652     /**
52653       \param filename Filename, as a C-string.
52654       \param dimw Width of the image buffer.
52655       \param dimh Height of the image buffer.
52656     **/
52657     CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
52658       return _load_rgb(0,filename,dimw,dimh);
52659     }
52660 
52661     //! Load image from a RGB file \newinstance.
52662     static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
52663       return CImg<T>().load_rgb(filename,dimw,dimh);
52664     }
52665 
52666     //! Load image from a RGB file \overloading.
52667     CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
52668       return _load_rgb(file,0,dimw,dimh);
52669     }
52670 
52671     //! Load image from a RGB file \newinstance.
52672     static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
52673       return CImg<T>().load_rgb(file,dimw,dimh);
52674     }
52675 
52676     CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
52677                        const unsigned int dimw, const unsigned int dimh) {
52678       if (!file && !filename)
52679         throw CImgArgumentException(_cimg_instance
52680                                     "load_rgb(): Specified filename is (null).",
52681                                     cimg_instance);
52682 
52683       if (!dimw || !dimh) return assign();
52684       const longT cimg_iobuffer = (longT)24*1024*1024;
52685       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52686       CImg<ucharT> raw;
52687       assign(dimw,dimh,1,3);
52688       T
52689         *ptr_r = data(0,0,0,0),
52690         *ptr_g = data(0,0,0,1),
52691         *ptr_b = data(0,0,0,2);
52692       for (longT to_read = (longT)size(); to_read>0; ) {
52693         raw.assign(std::min(to_read,cimg_iobuffer));
52694         cimg::fread(raw._data,raw._width,nfile);
52695         to_read-=raw._width;
52696         const unsigned char *ptrs = raw._data;
52697         for (ulongT off = raw._width/3UL; off; --off) {
52698           *(ptr_r++) = (T)*(ptrs++);
52699           *(ptr_g++) = (T)*(ptrs++);
52700           *(ptr_b++) = (T)*(ptrs++);
52701         }
52702       }
52703       if (!file) cimg::fclose(nfile);
52704       return *this;
52705     }
52706 
52707     //! Load image from a RGBA file.
52708     /**
52709        \param filename Filename, as a C-string.
52710        \param dimw Width of the image buffer.
52711        \param dimh Height of the image buffer.
52712     **/
52713     CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
52714       return _load_rgba(0,filename,dimw,dimh);
52715     }
52716 
52717     //! Load image from a RGBA file \newinstance.
52718     static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
52719       return CImg<T>().load_rgba(filename,dimw,dimh);
52720     }
52721 
52722     //! Load image from a RGBA file \overloading.
52723     CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
52724       return _load_rgba(file,0,dimw,dimh);
52725     }
52726 
52727     //! Load image from a RGBA file \newinstance.
52728     static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
52729       return CImg<T>().load_rgba(file,dimw,dimh);
52730     }
52731 
52732     CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
52733                         const unsigned int dimw, const unsigned int dimh) {
52734       if (!file && !filename)
52735         throw CImgArgumentException(_cimg_instance
52736                                     "load_rgba(): Specified filename is (null).",
52737                                     cimg_instance);
52738 
52739       if (!dimw || !dimh) return assign();
52740       const longT cimg_iobuffer = (longT)24*1024*1024;
52741       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
52742       CImg<ucharT> raw;
52743       assign(dimw,dimh,1,4);
52744       T
52745         *ptr_r = data(0,0,0,0),
52746         *ptr_g = data(0,0,0,1),
52747         *ptr_b = data(0,0,0,2),
52748         *ptr_a = data(0,0,0,3);
52749       for (longT to_read = (longT)size(); to_read>0; ) {
52750         raw.assign(std::min(to_read,cimg_iobuffer));
52751         cimg::fread(raw._data,raw._width,nfile);
52752         to_read-=raw._width;
52753         const unsigned char *ptrs = raw._data;
52754         for (ulongT off = raw._width/4UL; off; --off) {
52755           *(ptr_r++) = (T)*(ptrs++);
52756           *(ptr_g++) = (T)*(ptrs++);
52757           *(ptr_b++) = (T)*(ptrs++);
52758           *(ptr_a++) = (T)*(ptrs++);
52759         }
52760       }
52761       if (!file) cimg::fclose(nfile);
52762       return *this;
52763     }
52764 
52765     //! Load image from a TIFF file.
52766     /**
52767        \param filename Filename, as a C-string.
52768        \param first_frame First frame to read (for multi-pages tiff).
52769        \param last_frame Last frame to read (for multi-pages tiff).
52770        \param step_frame Step value of frame reading.
52771        \param[out] voxel_size Voxel size, as stored in the filename.
52772        \param[out] description Description, as stored in the filename.
52773        \note
52774        - libtiff support is enabled by defining the precompilation
52775         directive \c cimg_use_tif.
52776        - When libtiff is enabled, 2D and 3D (multipage) several
52777         channel per pixel are supported for
52778         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
52779        - If \c cimg_use_tif is not defined at compile time the
52780         function uses CImg<T>& load_other(const char*).
52781      **/
52782     CImg<T>& load_tiff(const char *const filename,
52783                        const unsigned int first_frame=0, const unsigned int last_frame=~0U,
52784                        const unsigned int step_frame=1,
52785                        float *const voxel_size=0,
52786                        CImg<charT> *const description=0) {
52787       if (!filename)
52788         throw CImgArgumentException(_cimg_instance
52789                                     "load_tiff(): Specified filename is (null).",
52790                                     cimg_instance);
52791 
52792       const unsigned int
52793         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
52794         nstep_frame = step_frame?step_frame:1;
52795       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
52796 
52797 #ifndef cimg_use_tiff
52798       cimg::unused(voxel_size,description);
52799       if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
52800         throw CImgArgumentException(_cimg_instance
52801                                     "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
52802                                     cimg_instance,
52803                                     filename);
52804       return load_other(filename);
52805 #else
52806 #if cimg_verbosity<3
52807       TIFFSetWarningHandler(0);
52808       TIFFSetErrorHandler(0);
52809 #endif
52810       TIFF *tif = TIFFOpen(filename,"r");
52811       if (tif) {
52812         unsigned int nb_images = 0;
52813         do ++nb_images; while (TIFFReadDirectory(tif));
52814         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
52815           cimg::warn(_cimg_instance
52816                      "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
52817                      cimg_instance,
52818                      filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
52819 
52820         if (nfirst_frame>=nb_images) return assign();
52821         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
52822         TIFFSetDirectory(tif,0);
52823         CImg<T> frame;
52824         for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
52825           frame._load_tiff(tif,l,voxel_size,description);
52826           if (l==nfirst_frame)
52827             assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
52828           if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
52829             resize(std::max(frame._width,_width),
52830                    std::max(frame._height,_height),-100,
52831                    std::max(frame._spectrum,_spectrum),0);
52832           draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
52833         }
52834         TIFFClose(tif);
52835       } else throw CImgIOException(_cimg_instance
52836                                    "load_tiff(): Failed to open file '%s'.",
52837                                    cimg_instance,
52838                                    filename);
52839       return *this;
52840 #endif
52841     }
52842 
52843     //! Load image from a TIFF file \newinstance.
52844     static CImg<T> get_load_tiff(const char *const filename,
52845                                  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
52846                                  const unsigned int step_frame=1,
52847                                  float *const voxel_size=0,
52848                                  CImg<charT> *const description=0) {
52849       return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description);
52850     }
52851 
52852     // (Original contribution by Jerome Boulanger).
52853 #ifdef cimg_use_tiff
52854     template<typename t>
52855     void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel,
52856                                  const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
52857       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
52858       if (buf) {
52859         for (unsigned int row = 0; row<ny; row+=th)
52860           for (unsigned int col = 0; col<nx; col+=tw) {
52861             if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
52862               _TIFFfree(buf); TIFFClose(tif);
52863               throw CImgIOException(_cimg_instance
52864                                     "load_tiff(): Invalid tile in file '%s'.",
52865                                     cimg_instance,
52866                                     TIFFFileName(tif));
52867             }
52868             const t *ptr = buf;
52869             for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
52870               for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
52871                 for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
52872                   (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]);
52873           }
52874         _TIFFfree(buf);
52875       }
52876     }
52877 
52878     template<typename t>
52879     void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel,
52880                                    const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
52881       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
52882       if (buf) {
52883         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
52884           for (unsigned int row = 0; row<ny; row+=th)
52885             for (unsigned int col = 0; col<nx; col+=tw) {
52886               if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
52887                 _TIFFfree(buf); TIFFClose(tif);
52888                 throw CImgIOException(_cimg_instance
52889                                       "load_tiff(): Invalid tile in file '%s'.",
52890                                       cimg_instance,
52891                                       TIFFFileName(tif));
52892               }
52893               const t *ptr = buf;
52894               for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
52895                 for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
52896                   (*this)(cc,rr,vv) = (T)*(ptr++);
52897             }
52898         _TIFFfree(buf);
52899       }
52900     }
52901 
52902     template<typename t>
52903     void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
52904       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
52905       if (buf) {
52906         uint32 row, rowsperstrip = (uint32)-1;
52907         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
52908         for (row = 0; row<ny; row+= rowsperstrip) {
52909           uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
52910           tstrip_t strip = TIFFComputeStrip(tif, row, 0);
52911           if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
52912             _TIFFfree(buf); TIFFClose(tif);
52913             throw CImgIOException(_cimg_instance
52914                                   "load_tiff(): Invalid strip in file '%s'.",
52915                                   cimg_instance,
52916                                   TIFFFileName(tif));
52917           }
52918           const t *ptr = buf;
52919           for (unsigned int rr = 0; rr<nrow; ++rr)
52920             for (unsigned int cc = 0; cc<nx; ++cc)
52921               for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++);
52922         }
52923         _TIFFfree(buf);
52924       }
52925     }
52926 
52927     template<typename t>
52928     void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
52929       t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
52930       if (buf) {
52931         uint32 row, rowsperstrip = (uint32)-1;
52932         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
52933         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
52934           for (row = 0; row<ny; row+= rowsperstrip) {
52935             uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
52936             tstrip_t strip = TIFFComputeStrip(tif, row, vv);
52937             if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
52938               _TIFFfree(buf); TIFFClose(tif);
52939               throw CImgIOException(_cimg_instance
52940                                     "load_tiff(): Invalid strip in file '%s'.",
52941                                     cimg_instance,
52942                                     TIFFFileName(tif));
52943             }
52944             const t *ptr = buf;
52945             for (unsigned int rr = 0;rr<nrow; ++rr)
52946               for (unsigned int cc = 0; cc<nx; ++cc)
52947                 (*this)(cc,row + rr,vv) = (T)*(ptr++);
52948           }
52949         _TIFFfree(buf);
52950       }
52951     }
52952 
52953     CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory,
52954                         float *const voxel_size, CImg<charT> *const description) {
52955       if (!TIFFSetDirectory(tif,directory)) return assign();
52956       uint16 samplesperpixel = 1, bitspersample = 8, photo = 0;
52957       uint16 sampleformat = 1;
52958       uint32 nx = 1, ny = 1;
52959       const char *const filename = TIFFFileName(tif);
52960       const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
52961       TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
52962       TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
52963       TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
52964       TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
52965       TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
52966       if (voxel_size) {
52967         const char *s_description = 0;
52968         float vx = 0, vy = 0, vz = 0;
52969         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) {
52970           const char *s_desc = std::strstr(s_description,"VX=");
52971           if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format
52972             voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz;
52973           }
52974           s_desc = std::strstr(s_description,"spacing=");
52975           if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format
52976             voxel_size[2] = vz;
52977           }
52978         }
52979         TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size);
52980         TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1);
52981         voxel_size[0] = 1.f/voxel_size[0];
52982         voxel_size[1] = 1.f/voxel_size[1];
52983       }
52984       if (description) {
52985         const char *s_description = 0;
52986         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
52987           CImg<charT>::string(s_description).move_to(*description);
52988       }
52989       const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
52990       assign(nx,ny,1,spectrum);
52991 
52992       if ((photo>=3 && sampleformat==1 &&
52993            (bitspersample==4 || bitspersample==8) &&
52994            (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
52995           (bitspersample==1 && samplesperpixel==1)) {
52996         // Special case for unsigned color images.
52997         uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
52998         if (!raster) {
52999           _TIFFfree(raster); TIFFClose(tif);
53000           throw CImgException(_cimg_instance
53001                               "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
53002                               cimg_instance,
53003                               cimg::strbuffersize(nx*ny*sizeof(uint32)),filename);
53004         }
53005         TIFFReadRGBAImage(tif,nx,ny,raster,0);
53006         switch (spectrum) {
53007         case 1 :
53008           cimg_forXY(*this,x,y)
53009             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
53010           break;
53011         case 3 :
53012           cimg_forXY(*this,x,y) {
53013             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
53014             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 -y) + x]);
53015             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 -y) + x]);
53016           }
53017           break;
53018         case 4 :
53019           cimg_forXY(*this,x,y) {
53020             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
53021             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
53022             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
53023             (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]);
53024           }
53025           break;
53026         }
53027         _TIFFfree(raster);
53028       } else { // Other cases
53029         uint16 config;
53030         TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
53031         if (TIFFIsTiled(tif)) {
53032           uint32 tw = 1, th = 1;
53033           TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
53034           TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
53035           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
53036             case 8 :
53037               if (sampleformat==SAMPLEFORMAT_UINT)
53038                 _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
53039               else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
53040               break;
53041             case 16 :
53042               if (sampleformat==SAMPLEFORMAT_UINT)
53043                 _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
53044               else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
53045               break;
53046             case 32 :
53047               if (sampleformat==SAMPLEFORMAT_UINT)
53048                 _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
53049               else if (sampleformat==SAMPLEFORMAT_INT)
53050                 _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
53051               else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
53052               break;
53053             case 64 :
53054               if (sampleformat==SAMPLEFORMAT_UINT)
53055                 _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
53056               else if (sampleformat==SAMPLEFORMAT_INT)
53057                 _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th);
53058               else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th);
53059               break;
53060             } else switch (bitspersample) {
53061             case 8 :
53062               if (sampleformat==SAMPLEFORMAT_UINT)
53063                 _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
53064               else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
53065               break;
53066             case 16 :
53067               if (sampleformat==SAMPLEFORMAT_UINT)
53068                 _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
53069               else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
53070               break;
53071             case 32 :
53072               if (sampleformat==SAMPLEFORMAT_UINT)
53073                 _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
53074               else if (sampleformat==SAMPLEFORMAT_INT)
53075                 _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
53076               else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
53077               break;
53078             case 64 :
53079               if (sampleformat==SAMPLEFORMAT_UINT)
53080                 _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
53081               else if (sampleformat==SAMPLEFORMAT_INT)
53082                 _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th);
53083               else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th);
53084               break;
53085             }
53086         } else {
53087           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
53088             case 8 :
53089               if (sampleformat==SAMPLEFORMAT_UINT)
53090                 _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
53091               else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
53092               break;
53093             case 16 :
53094               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
53095               else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
53096               break;
53097             case 32 :
53098               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
53099               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
53100               else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
53101               break;
53102             case 64 :
53103               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny);
53104               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny);
53105               else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny);
53106               break;
53107             } else switch (bitspersample) {
53108             case 8 :
53109               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
53110               else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
53111               break;
53112             case 16 :
53113               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
53114               else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
53115               break;
53116             case 32 :
53117               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
53118               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
53119               else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
53120               break;
53121             case 64 :
53122               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny);
53123               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny);
53124               else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny);
53125               break;
53126             }
53127         }
53128       }
53129       return *this;
53130     }
53131 #endif
53132 
53133     //! Load image from a MINC2 file.
53134     /**
53135         \param filename Filename, as a C-string.
53136     **/
53137     // (Original code by Haz-Edine Assemlal).
53138     CImg<T>& load_minc2(const char *const filename) {
53139       if (!filename)
53140         throw CImgArgumentException(_cimg_instance
53141                                     "load_minc2(): Specified filename is (null).",
53142                                     cimg_instance);
53143 #ifndef cimg_use_minc2
53144       return load_other(filename);
53145 #else
53146       minc::minc_1_reader rdr;
53147       rdr.open(filename);
53148       assign(rdr.ndim(1)?rdr.ndim(1):1,
53149              rdr.ndim(2)?rdr.ndim(2):1,
53150              rdr.ndim(3)?rdr.ndim(3):1,
53151              rdr.ndim(4)?rdr.ndim(4):1);
53152       if (pixel_type()==cimg::type<unsigned char>::string())
53153         rdr.setup_read_byte();
53154       else if (pixel_type()==cimg::type<int>::string())
53155         rdr.setup_read_int();
53156       else if (pixel_type()==cimg::type<double>::string())
53157         rdr.setup_read_double();
53158       else
53159         rdr.setup_read_float();
53160       minc::load_standard_volume(rdr,this->_data);
53161       return *this;
53162 #endif
53163     }
53164 
53165     //! Load image from a MINC2 file \newinstance.
53166     static CImg<T> get_load_minc2(const char *const filename) {
53167       return CImg<T>().load_analyze(filename);
53168     }
53169 
53170     //! Load image from an ANALYZE7.5/NIFTI file.
53171     /**
53172        \param filename Filename, as a C-string.
53173        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
53174     **/
53175     CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
53176       return _load_analyze(0,filename,voxel_size);
53177     }
53178 
53179     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
53180     static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
53181       return CImg<T>().load_analyze(filename,voxel_size);
53182     }
53183 
53184     //! Load image from an ANALYZE7.5/NIFTI file \overloading.
53185     CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
53186       return _load_analyze(file,0,voxel_size);
53187     }
53188 
53189     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
53190     static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
53191       return CImg<T>().load_analyze(file,voxel_size);
53192     }
53193 
53194     CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
53195       if (!file && !filename)
53196         throw CImgArgumentException(_cimg_instance
53197                                     "load_analyze(): Specified filename is (null).",
53198                                     cimg_instance);
53199 
53200       std::FILE *nfile_header = 0, *nfile = 0;
53201       if (!file) {
53202         CImg<charT> body(1024);
53203         const char *const ext = cimg::split_filename(filename,body);
53204         if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file
53205           nfile_header = cimg::fopen(filename,"rb");
53206           cimg_sprintf(body._data + std::strlen(body),".img");
53207           nfile = cimg::fopen(body,"rb");
53208         } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file
53209           nfile = cimg::fopen(filename,"rb");
53210           cimg_sprintf(body._data + std::strlen(body),".hdr");
53211           nfile_header = cimg::fopen(body,"rb");
53212         } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file
53213       } else nfile_header = nfile = file; // File is a Niftii file
53214       if (!nfile || !nfile_header)
53215         throw CImgIOException(_cimg_instance
53216                               "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
53217                               cimg_instance,
53218                               filename?filename:"(FILE*)");
53219 
53220       // Read header.
53221       bool endian = false;
53222       unsigned int header_size;
53223       cimg::fread(&header_size,1,nfile_header);
53224       if (!header_size)
53225         throw CImgIOException(_cimg_instance
53226                               "load_analyze(): Invalid zero-size header in file '%s'.",
53227                               cimg_instance,
53228                               filename?filename:"(FILE*)");
53229       if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
53230 
53231       unsigned char *const header = new unsigned char[header_size];
53232       cimg::fread(header + 4,header_size - 4,nfile_header);
53233       if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
53234       if (endian) {
53235         cimg::invert_endianness((short*)(header + 40),5);
53236         cimg::invert_endianness((short*)(header + 70),1);
53237         cimg::invert_endianness((short*)(header + 72),1);
53238         cimg::invert_endianness((float*)(header + 76),4);
53239         cimg::invert_endianness((float*)(header + 108),1);
53240         cimg::invert_endianness((float*)(header + 112),1);
53241       }
53242 
53243       if (nfile_header==nfile) {
53244         const unsigned int vox_offset = (unsigned int)*(float*)(header + 108);
53245         std::fseek(nfile,vox_offset,SEEK_SET);
53246       }
53247 
53248       unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
53249       if (!dim[0])
53250         cimg::warn(_cimg_instance
53251                    "load_analyze(): File '%s' defines an image with zero dimensions.",
53252                    cimg_instance,
53253                    filename?filename:"(FILE*)");
53254 
53255       if (dim[0]>4)
53256         cimg::warn(_cimg_instance
53257                    "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
53258                    cimg_instance,
53259                    filename?filename:"(FILE*)",dim[0]);
53260 
53261       if (dim[0]>=1) dimx = dim[1];
53262       if (dim[0]>=2) dimy = dim[2];
53263       if (dim[0]>=3) dimz = dim[3];
53264       if (dim[0]>=4) dimv = dim[4];
53265       float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1;
53266       const unsigned short datatype = *(unsigned short*)(header + 70);
53267       if (voxel_size) {
53268         const float *vsize = (float*)(header + 76);
53269         voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
53270       }
53271       delete[] header;
53272 
53273       // Read pixel data.
53274       assign(dimx,dimy,dimz,dimv);
53275       const size_t pdim = (size_t)dimx*dimy*dimz*dimv;
53276       switch (datatype) {
53277       case 2 : {
53278         unsigned char *const buffer = new unsigned char[pdim];
53279         cimg::fread(buffer,pdim,nfile);
53280         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53281         delete[] buffer;
53282       } break;
53283       case 4 : {
53284         short *const buffer = new short[pdim];
53285         cimg::fread(buffer,pdim,nfile);
53286         if (endian) cimg::invert_endianness(buffer,pdim);
53287         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53288         delete[] buffer;
53289       } break;
53290       case 8 : {
53291         int *const buffer = new int[pdim];
53292         cimg::fread(buffer,pdim,nfile);
53293         if (endian) cimg::invert_endianness(buffer,pdim);
53294         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53295         delete[] buffer;
53296       } break;
53297       case 16 : {
53298         float *const buffer = new float[pdim];
53299         cimg::fread(buffer,pdim,nfile);
53300         if (endian) cimg::invert_endianness(buffer,pdim);
53301         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53302         delete[] buffer;
53303       } break;
53304       case 64 : {
53305         double *const buffer = new double[pdim];
53306         cimg::fread(buffer,pdim,nfile);
53307         if (endian) cimg::invert_endianness(buffer,pdim);
53308         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
53309         delete[] buffer;
53310       } break;
53311       default :
53312         if (!file) cimg::fclose(nfile);
53313         throw CImgIOException(_cimg_instance
53314                               "load_analyze(): Unable to load datatype %d in file '%s'",
53315                               cimg_instance,
53316                               datatype,filename?filename:"(FILE*)");
53317       }
53318       if (!file) cimg::fclose(nfile);
53319       return *this;
53320     }
53321 
53322     //! Load image from a .cimg[z] file.
53323     /**
53324       \param filename Filename, as a C-string.
53325       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
53326       \param align Appending alignment.
53327     **/
53328     CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
53329       CImgList<T> list;
53330       list.load_cimg(filename);
53331       if (list._width==1) return list[0].move_to(*this);
53332       return assign(list.get_append(axis,align));
53333     }
53334 
53335     //! Load image from a .cimg[z] file \newinstance
53336     static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
53337       return CImg<T>().load_cimg(filename,axis,align);
53338     }
53339 
53340     //! Load image from a .cimg[z] file \overloading.
53341     CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
53342       CImgList<T> list;
53343       list.load_cimg(file);
53344       if (list._width==1) return list[0].move_to(*this);
53345       return assign(list.get_append(axis,align));
53346     }
53347 
53348     //! Load image from a .cimg[z] file \newinstance
53349     static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
53350       return CImg<T>().load_cimg(file,axis,align);
53351     }
53352 
53353     //! Load sub-images of a .cimg file.
53354     /**
53355       \param filename Filename, as a C-string.
53356       \param n0 Starting frame.
53357       \param n1 Ending frame (~0U for max).
53358       \param x0 X-coordinate of the starting sub-image vertex.
53359       \param y0 Y-coordinate of the starting sub-image vertex.
53360       \param z0 Z-coordinate of the starting sub-image vertex.
53361       \param c0 C-coordinate of the starting sub-image vertex.
53362       \param x1 X-coordinate of the ending sub-image vertex (~0U for max).
53363       \param y1 Y-coordinate of the ending sub-image vertex (~0U for max).
53364       \param z1 Z-coordinate of the ending sub-image vertex (~0U for max).
53365       \param c1 C-coordinate of the ending sub-image vertex (~0U for max).
53366       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
53367       \param align Appending alignment.
53368     **/
53369     CImg<T>& load_cimg(const char *const filename,
53370                        const unsigned int n0, const unsigned int n1,
53371                        const unsigned int x0, const unsigned int y0,
53372                        const unsigned int z0, const unsigned int c0,
53373                        const unsigned int x1, const unsigned int y1,
53374                        const unsigned int z1, const unsigned int c1,
53375                        const char axis='z', const float align=0) {
53376       CImgList<T> list;
53377       list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
53378       if (list._width==1) return list[0].move_to(*this);
53379       return assign(list.get_append(axis,align));
53380     }
53381 
53382     //! Load sub-images of a .cimg file \newinstance.
53383     static CImg<T> get_load_cimg(const char *const filename,
53384                                  const unsigned int n0, const unsigned int n1,
53385                                  const unsigned int x0, const unsigned int y0,
53386                                  const unsigned int z0, const unsigned int c0,
53387                                  const unsigned int x1, const unsigned int y1,
53388                                  const unsigned int z1, const unsigned int c1,
53389                                  const char axis='z', const float align=0) {
53390       return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
53391     }
53392 
53393     //! Load sub-images of a .cimg file \overloading.
53394     CImg<T>& load_cimg(std::FILE *const file,
53395                        const unsigned int n0, const unsigned int n1,
53396                        const unsigned int x0, const unsigned int y0,
53397                        const unsigned int z0, const unsigned int c0,
53398                        const unsigned int x1, const unsigned int y1,
53399                        const unsigned int z1, const unsigned int c1,
53400                        const char axis='z', const float align=0) {
53401       CImgList<T> list;
53402       list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
53403       if (list._width==1) return list[0].move_to(*this);
53404       return assign(list.get_append(axis,align));
53405     }
53406 
53407     //! Load sub-images of a .cimg file \newinstance.
53408     static CImg<T> get_load_cimg(std::FILE *const file,
53409                                  const unsigned int n0, const unsigned int n1,
53410                                  const unsigned int x0, const unsigned int y0,
53411                                  const unsigned int z0, const unsigned int c0,
53412                                  const unsigned int x1, const unsigned int y1,
53413                                  const unsigned int z1, const unsigned int c1,
53414                                  const char axis='z', const float align=0) {
53415       return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
53416     }
53417 
53418     //! Load image from an INRIMAGE-4 file.
53419     /**
53420        \param filename Filename, as a C-string.
53421        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
53422     **/
53423     CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
53424       return _load_inr(0,filename,voxel_size);
53425     }
53426 
53427     //! Load image from an INRIMAGE-4 file \newinstance.
53428     static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
53429       return CImg<T>().load_inr(filename,voxel_size);
53430     }
53431 
53432     //! Load image from an INRIMAGE-4 file \overloading.
53433     CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
53434       return _load_inr(file,0,voxel_size);
53435     }
53436 
53437     //! Load image from an INRIMAGE-4 file \newinstance.
53438     static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
53439       return CImg<T>().load_inr(file,voxel_size);
53440     }
53441 
53442     static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
53443       CImg<charT> item(1024), tmp1(64), tmp2(64);
53444       *item = *tmp1 = *tmp2 = 0;
53445       out[0] = std::fscanf(file,"%63s",item._data);
53446       out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
53447       if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
53448         throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
53449                               pixel_type());
53450 
53451       while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) {
53452         cimg_sscanf(item," XDIM%*[^0-9]%d",out);
53453         cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1);
53454         cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2);
53455         cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3);
53456         cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6);
53457         if (voxel_size) {
53458           cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
53459           cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1);
53460           cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2);
53461         }
53462         if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1;
53463         switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) {
53464         case 0 : break;
53465         case 2 :
53466           out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0;
53467           std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough
53468         case 1 :
53469           if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5))  out[4] = 0;
53470           if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
53471           if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
53472           if (out[4]>=0) break; // fallthrough
53473         default :
53474           throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
53475                                 pixel_type(),
53476                                 tmp2._data);
53477         }
53478       }
53479       if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
53480         throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
53481                               pixel_type(),
53482                               out[0],out[1],out[2],out[3]);
53483       if (out[4]<0 || out[5]<0)
53484         throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
53485                               pixel_type());
53486       if (out[6]<0)
53487         throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
53488                               pixel_type());
53489       if (out[7]<0)
53490         throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
53491                               pixel_type());
53492     }
53493 
53494     CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
53495 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
53496      if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
53497         Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \
53498         cimg_forYZ(*this,y,z) { \
53499             cimg::fread(val,fopt[0]*fopt[3],nfile); \
53500             if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
53501             xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
53502           } \
53503         delete[] val; \
53504         loaded = true; \
53505       }
53506 
53507       if (!file && !filename)
53508         throw CImgArgumentException(_cimg_instance
53509                                     "load_inr(): Specified filename is (null).",
53510                                     cimg_instance);
53511 
53512       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53513       int fopt[8], endian = cimg::endianness()?1:0;
53514       bool loaded = false;
53515       if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
53516       _load_inr_header(nfile,fopt,voxel_size);
53517       assign(fopt[0],fopt[1],fopt[2],fopt[3]);
53518       _cimg_load_inr_case(0,0,8,unsigned char);
53519       _cimg_load_inr_case(0,1,8,char);
53520       _cimg_load_inr_case(0,0,16,unsigned short);
53521       _cimg_load_inr_case(0,1,16,short);
53522       _cimg_load_inr_case(0,0,32,unsigned int);
53523       _cimg_load_inr_case(0,1,32,int);
53524       _cimg_load_inr_case(1,0,32,float);
53525       _cimg_load_inr_case(1,1,32,float);
53526       _cimg_load_inr_case(1,0,64,double);
53527       _cimg_load_inr_case(1,1,64,double);
53528       if (!loaded) {
53529         if (!file) cimg::fclose(nfile);
53530         throw CImgIOException(_cimg_instance
53531                               "load_inr(): Unknown pixel type defined in file '%s'.",
53532                               cimg_instance,
53533                               filename?filename:"(FILE*)");
53534       }
53535       if (!file) cimg::fclose(nfile);
53536       return *this;
53537     }
53538 
53539     //! Load image from a EXR file.
53540     /**
53541       \param filename Filename, as a C-string.
53542     **/
53543     CImg<T>& load_exr(const char *const filename) {
53544       if (!filename)
53545         throw CImgArgumentException(_cimg_instance
53546                                     "load_exr(): Specified filename is (null).",
53547                                     cimg_instance);
53548 #if defined(cimg_use_openexr)
53549       Imf::RgbaInputFile file(filename);
53550       Imath::Box2i dw = file.dataWindow();
53551       const int
53552         inwidth = dw.max.x - dw.min.x + 1,
53553         inheight = dw.max.y - dw.min.y + 1;
53554       Imf::Array2D<Imf::Rgba> pixels;
53555       pixels.resizeErase(inheight,inwidth);
53556       file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
53557       file.readPixels(dw.min.y, dw.max.y);
53558       assign(inwidth,inheight,1,4);
53559       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);
53560       cimg_forXY(*this,x,y) {
53561         *(ptr_r++) = (T)pixels[y][x].r;
53562         *(ptr_g++) = (T)pixels[y][x].g;
53563         *(ptr_b++) = (T)pixels[y][x].b;
53564         *(ptr_a++) = (T)pixels[y][x].a;
53565       }
53566       return *this;
53567 #elif defined(cimg_use_tinyexr)
53568       float *res;
53569       const char *err = 0;
53570       int width = 0, height = 0;
53571       const int ret = LoadEXR(&res,&width,&height,filename,&err);
53572       if (ret) throw CImgIOException(_cimg_instance
53573                                      "load_exr(): Unable to load EXR file '%s'.",
53574                                      cimg_instance,filename);
53575       CImg<floatT>(res,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
53576       std::free(res);
53577       return *this;
53578 #else
53579       return load_other(filename);
53580 #endif
53581     }
53582 
53583     //! Load image from a EXR file \newinstance.
53584     static CImg<T> get_load_exr(const char *const filename) {
53585       return CImg<T>().load_exr(filename);
53586     }
53587 
53588     //! Load image from a PANDORE-5 file.
53589     /**
53590       \param filename Filename, as a C-string.
53591     **/
53592     CImg<T>& load_pandore(const char *const filename) {
53593       return _load_pandore(0,filename);
53594     }
53595 
53596     //! Load image from a PANDORE-5 file \newinstance.
53597     static CImg<T> get_load_pandore(const char *const filename) {
53598       return CImg<T>().load_pandore(filename);
53599     }
53600 
53601     //! Load image from a PANDORE-5 file \overloading.
53602     CImg<T>& load_pandore(std::FILE *const file) {
53603       return _load_pandore(file,0);
53604     }
53605 
53606     //! Load image from a PANDORE-5 file \newinstance.
53607     static CImg<T> get_load_pandore(std::FILE *const file) {
53608       return CImg<T>().load_pandore(file);
53609     }
53610 
53611     CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
53612 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
53613         cimg::fread(dims,nbdim,nfile); \
53614         if (endian) cimg::invert_endianness(dims,nbdim); \
53615         assign(nwidth,nheight,ndepth,ndim); \
53616         const size_t siz = size(); \
53617         stype *buffer = new stype[siz]; \
53618         cimg::fread(buffer,siz,nfile); \
53619         if (endian) cimg::invert_endianness(buffer,siz); \
53620         T *ptrd = _data; \
53621         cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
53622         buffer-=siz; \
53623         delete[] buffer
53624 
53625 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
53626         if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
53627         else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
53628         else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
53629         else throw CImgIOException(_cimg_instance \
53630                                    "load_pandore(): Unknown pixel datatype in file '%s'.", \
53631                                    cimg_instance, \
53632                                    filename?filename:"(FILE*)"); }
53633       if (!file && !filename)
53634         throw CImgArgumentException(_cimg_instance
53635                                     "load_pandore(): Specified filename is (null).",
53636                                     cimg_instance);
53637 
53638       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53639       CImg<charT> header(32);
53640       cimg::fread(header._data,12,nfile);
53641       if (cimg::strncasecmp("PANDORE",header,7)) {
53642         if (!file) cimg::fclose(nfile);
53643         throw CImgIOException(_cimg_instance
53644                               "load_pandore(): PANDORE header not found in file '%s'.",
53645                               cimg_instance,
53646                               filename?filename:"(FILE*)");
53647       }
53648       unsigned int imageid, dims[8] = { 0 };
53649       int ptbuf[4] = { 0 };
53650       cimg::fread(&imageid,1,nfile);
53651       const bool endian = imageid>255;
53652       if (endian) cimg::invert_endianness(imageid);
53653       cimg::fread(header._data,20,nfile);
53654 
53655       switch (imageid) {
53656       case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
53657       case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
53658       case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
53659       case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
53660       case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
53661       case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
53662       case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
53663       case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
53664       case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
53665       case 11 : { // Region 1D
53666         cimg::fread(dims,3,nfile);
53667         if (endian) cimg::invert_endianness(dims,3);
53668         assign(dims[1],1,1,1);
53669         const unsigned siz = size();
53670         if (dims[2]<256) {
53671           unsigned char *buffer = new unsigned char[siz];
53672           cimg::fread(buffer,siz,nfile);
53673           T *ptrd = _data;
53674           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53675           buffer-=siz;
53676           delete[] buffer;
53677         } else {
53678           if (dims[2]<65536) {
53679             unsigned short *buffer = new unsigned short[siz];
53680             cimg::fread(buffer,siz,nfile);
53681             if (endian) cimg::invert_endianness(buffer,siz);
53682             T *ptrd = _data;
53683             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53684             buffer-=siz;
53685             delete[] buffer;
53686           } else {
53687             unsigned int *buffer = new unsigned int[siz];
53688             cimg::fread(buffer,siz,nfile);
53689             if (endian) cimg::invert_endianness(buffer,siz);
53690             T *ptrd = _data;
53691             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53692             buffer-=siz;
53693             delete[] buffer;
53694           }
53695         }
53696       }
53697         break;
53698       case 12 : { // Region 2D
53699         cimg::fread(dims,4,nfile);
53700         if (endian) cimg::invert_endianness(dims,4);
53701         assign(dims[2],dims[1],1,1);
53702         const size_t siz = size();
53703         if (dims[3]<256) {
53704           unsigned char *buffer = new unsigned char[siz];
53705           cimg::fread(buffer,siz,nfile);
53706           T *ptrd = _data;
53707           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53708           buffer-=siz;
53709           delete[] buffer;
53710         } else {
53711           if (dims[3]<65536) {
53712             unsigned short *buffer = new unsigned short[siz];
53713             cimg::fread(buffer,siz,nfile);
53714             if (endian) cimg::invert_endianness(buffer,siz);
53715             T *ptrd = _data;
53716             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53717             buffer-=siz;
53718             delete[] buffer;
53719           } else {
53720             unsigned int *buffer = new unsigned int[siz];
53721             cimg::fread(buffer,siz,nfile);
53722             if (endian) cimg::invert_endianness(buffer,siz);
53723             T *ptrd = _data;
53724             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53725             buffer-=siz;
53726             delete[] buffer;
53727           }
53728         }
53729       }
53730         break;
53731       case 13 : { // Region 3D
53732         cimg::fread(dims,5,nfile);
53733         if (endian) cimg::invert_endianness(dims,5);
53734         assign(dims[3],dims[2],dims[1],1);
53735         const size_t siz = size();
53736         if (dims[4]<256) {
53737           unsigned char *buffer = new unsigned char[siz];
53738           cimg::fread(buffer,siz,nfile);
53739           T *ptrd = _data;
53740           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53741           buffer-=siz;
53742           delete[] buffer;
53743         } else {
53744           if (dims[4]<65536) {
53745             unsigned short *buffer = new unsigned short[siz];
53746             cimg::fread(buffer,siz,nfile);
53747             if (endian) cimg::invert_endianness(buffer,siz);
53748             T *ptrd = _data;
53749             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53750             buffer-=siz;
53751             delete[] buffer;
53752           } else {
53753             unsigned int *buffer = new unsigned int[siz];
53754             cimg::fread(buffer,siz,nfile);
53755             if (endian) cimg::invert_endianness(buffer,siz);
53756             T *ptrd = _data;
53757             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
53758             buffer-=siz;
53759             delete[] buffer;
53760           }
53761         }
53762       }
53763         break;
53764       case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
53765       case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
53766       case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
53767       case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
53768       case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
53769       case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
53770       case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
53771       case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break;
53772       case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
53773       case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
53774       case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
53775       case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
53776       case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
53777       case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
53778       case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
53779         break;
53780       case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
53781       case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
53782         break;
53783       case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
53784       case 34 : { // Points 1D
53785         cimg::fread(ptbuf,1,nfile);
53786         if (endian) cimg::invert_endianness(ptbuf,1);
53787         assign(1); (*this)(0) = (T)ptbuf[0];
53788       } break;
53789       case 35 : { // Points 2D
53790         cimg::fread(ptbuf,2,nfile);
53791         if (endian) cimg::invert_endianness(ptbuf,2);
53792         assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
53793       } break;
53794       case 36 : { // Points 3D
53795         cimg::fread(ptbuf,3,nfile);
53796         if (endian) cimg::invert_endianness(ptbuf,3);
53797         assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
53798       } break;
53799       default :
53800         if (!file) cimg::fclose(nfile);
53801         throw CImgIOException(_cimg_instance
53802                               "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
53803                               cimg_instance,
53804                               imageid,filename?filename:"(FILE*)");
53805       }
53806       if (!file) cimg::fclose(nfile);
53807       return *this;
53808     }
53809 
53810     //! Load image from a PAR-REC (Philips) file.
53811     /**
53812       \param filename Filename, as a C-string.
53813       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
53814       \param align Appending alignment.
53815     **/
53816     CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
53817       CImgList<T> list;
53818       list.load_parrec(filename);
53819       if (list._width==1) return list[0].move_to(*this);
53820       return assign(list.get_append(axis,align));
53821     }
53822 
53823     //! Load image from a PAR-REC (Philips) file \newinstance.
53824     static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
53825       return CImg<T>().load_parrec(filename,axis,align);
53826     }
53827 
53828     //! Load image from a raw binary file.
53829     /**
53830       \param filename Filename, as a C-string.
53831       \param size_x Width of the image buffer.
53832       \param size_y Height of the image buffer.
53833       \param size_z Depth of the image buffer.
53834       \param size_c Spectrum of the image buffer.
53835       \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
53836       \param invert_endianness Tells if the endianness of the image buffer must be inverted.
53837       \param offset Starting offset of the read in the specified file.
53838     **/
53839     CImg<T>& load_raw(const char *const filename,
53840                       const unsigned int size_x=0, const unsigned int size_y=1,
53841                       const unsigned int size_z=1, const unsigned int size_c=1,
53842                       const bool is_multiplexed=false, const bool invert_endianness=false,
53843                       const ulongT offset=0) {
53844       return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
53845     }
53846 
53847     //! Load image from a raw binary file \newinstance.
53848     static CImg<T> get_load_raw(const char *const filename,
53849                                 const unsigned int size_x=0, const unsigned int size_y=1,
53850                                 const unsigned int size_z=1, const unsigned int size_c=1,
53851                                 const bool is_multiplexed=false, const bool invert_endianness=false,
53852                                 const ulongT offset=0) {
53853       return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
53854     }
53855 
53856     //! Load image from a raw binary file \overloading.
53857     CImg<T>& load_raw(std::FILE *const file,
53858                       const unsigned int size_x=0, const unsigned int size_y=1,
53859                       const unsigned int size_z=1, const unsigned int size_c=1,
53860                       const bool is_multiplexed=false, const bool invert_endianness=false,
53861                       const ulongT offset=0) {
53862       return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
53863     }
53864 
53865     //! Load image from a raw binary file \newinstance.
53866     static CImg<T> get_load_raw(std::FILE *const file,
53867                                 const unsigned int size_x=0, const unsigned int size_y=1,
53868                                 const unsigned int size_z=1, const unsigned int size_c=1,
53869                                 const bool is_multiplexed=false, const bool invert_endianness=false,
53870                                 const ulongT offset=0) {
53871       return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
53872     }
53873 
53874     CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
53875                        const unsigned int size_x, const unsigned int size_y,
53876                        const unsigned int size_z, const unsigned int size_c,
53877                        const bool is_multiplexed, const bool invert_endianness,
53878                        const ulongT offset) {
53879       if (!file && !filename)
53880         throw CImgArgumentException(_cimg_instance
53881                                     "load_raw(): Specified filename is (null).",
53882                                     cimg_instance);
53883       if (cimg::is_directory(filename))
53884         throw CImgArgumentException(_cimg_instance
53885                                     "load_raw(): Specified filename '%s' is a directory.",
53886                                     cimg_instance,filename);
53887       const bool is_bool = pixel_type()==cimg::type<bool>::string();
53888       ulongT siz = (ulongT)size_x*size_y*size_z*size_c;
53889       unsigned int
53890         _size_x = size_x,
53891         _size_y = size_y,
53892         _size_z = size_z,
53893         _size_c = size_c;
53894       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
53895       if (!siz) {  // Retrieve file size
53896         const longT fpos = cimg::ftell(nfile);
53897         if (fpos<0) throw CImgArgumentException(_cimg_instance
53898                                                 "load_raw(): Cannot determine size of input file '%s'.",
53899                                                 cimg_instance,filename?filename:"(FILE*)");
53900         cimg::fseek(nfile,0,SEEK_END);
53901         siz = cimg::ftell(nfile);
53902         if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; }
53903         else _size_y = (unsigned int)(siz*8);
53904         _size_x = _size_z = _size_c = 1;
53905         cimg::fseek(nfile,fpos,SEEK_SET);
53906       }
53907       cimg::fseek(nfile,offset,SEEK_SET);
53908       assign(_size_x,_size_y,_size_z,_size_c,0);
53909 
53910       if (is_bool) { // Boolean data (bitwise)
53911         unsigned char *const buf = new unsigned char[siz];
53912         cimg::fread(buf,siz,nfile);
53913         _uchar2bool(buf,siz,is_multiplexed);
53914         delete[] buf;
53915       } else { // Non-boolean data
53916         if (siz && (!is_multiplexed || size_c==1)) { // Non-multiplexed
53917           cimg::fread(_data,siz,nfile);
53918           if (invert_endianness) cimg::invert_endianness(_data,siz);
53919         } else if (siz) { // Multiplexed
53920           CImg<T> buf(1,1,1,_size_c);
53921           cimg_forXYZ(*this,x,y,z) {
53922             cimg::fread(buf._data,_size_c,nfile);
53923             if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
53924             set_vector_at(buf,x,y,z);
53925           }
53926         }
53927       }
53928       if (!file) cimg::fclose(nfile);
53929       return *this;
53930     }
53931 
53932     //! Load image sequence from a YUV file.
53933     /**
53934       \param filename Filename, as a C-string.
53935       \param size_x Width of the frames.
53936       \param size_y Height of the frames.
53937       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
53938       \param first_frame Index of the first frame to read.
53939       \param last_frame Index of the last frame to read.
53940       \param step_frame Step value for frame reading.
53941       \param yuv2rgb Tells if the YUV to RGB transform must be applied.
53942       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
53943     **/
53944     CImg<T>& load_yuv(const char *const filename,
53945                       const unsigned int size_x, const unsigned int size_y=1,
53946                       const unsigned int chroma_subsampling=444,
53947                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
53948                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
53949       return get_load_yuv(filename,size_x,size_y,chroma_subsampling,
53950                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
53951     }
53952 
53953     //! Load image sequence from a YUV file \newinstance.
53954     static CImg<T> get_load_yuv(const char *const filename,
53955                                 const unsigned int size_x, const unsigned int size_y=1,
53956                                 const unsigned int chroma_subsampling=444,
53957                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
53958                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
53959       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
53960                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
53961     }
53962 
53963     //! Load image sequence from a YUV file \overloading.
53964     CImg<T>& load_yuv(std::FILE *const file,
53965                       const unsigned int size_x, const unsigned int size_y=1,
53966                       const unsigned int chroma_subsampling=444,
53967                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
53968                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
53969       return get_load_yuv(file,size_x,size_y,chroma_subsampling,
53970                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
53971     }
53972 
53973     //! Load image sequence from a YUV file \newinstance.
53974     static CImg<T> get_load_yuv(std::FILE *const file,
53975                                 const unsigned int size_x, const unsigned int size_y=1,
53976                                 const unsigned int chroma_subsampling=444,
53977                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
53978                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
53979       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
53980                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
53981     }
53982 
53983     //! Load 3D object from a .OFF file.
53984     /**
53985         \param[out] primitives Primitives data of the 3D object.
53986         \param[out] colors Colors data of the 3D object.
53987         \param filename Filename, as a C-string.
53988     **/
53989     template<typename tf, typename tc>
53990     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
53991       return _load_off(primitives,colors,0,filename);
53992     }
53993 
53994     //! Load 3D object from a .OFF file \newinstance.
53995     template<typename tf, typename tc>
53996     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
53997       return CImg<T>().load_off(primitives,colors,filename);
53998     }
53999 
54000     //! Load 3D object from a .OFF file \overloading.
54001     template<typename tf, typename tc>
54002     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
54003       return _load_off(primitives,colors,file,0);
54004     }
54005 
54006     //! Load 3D object from a .OFF file \newinstance.
54007     template<typename tf, typename tc>
54008     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
54009       return CImg<T>().load_off(primitives,colors,file);
54010     }
54011 
54012     template<typename tf, typename tc>
54013     CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
54014                        std::FILE *const file, const char *const filename) {
54015       if (!file && !filename)
54016         throw CImgArgumentException(_cimg_instance
54017                                     "load_off(): Specified filename is (null).",
54018                                     cimg_instance);
54019 
54020       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
54021       unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
54022       CImg<charT> line(256); *line = 0;
54023       int err;
54024 
54025       // Skip comments, and read magic string OFF
54026       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
54027       if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
54028         if (!file) cimg::fclose(nfile);
54029         throw CImgIOException(_cimg_instance
54030                               "load_off(): OFF header not found in file '%s'.",
54031                               cimg_instance,
54032                               filename?filename:"(FILE*)");
54033       }
54034       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
54035       if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
54036         if (!file) cimg::fclose(nfile);
54037         throw CImgIOException(_cimg_instance
54038                               "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
54039                               cimg_instance,
54040                               filename?filename:"(FILE*)");
54041       }
54042 
54043       // Read points data
54044       assign(nb_points,3);
54045       float X = 0, Y = 0, Z = 0;
54046       cimg_forX(*this,l) {
54047         do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
54048         if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
54049           if (!file) cimg::fclose(nfile);
54050           throw CImgIOException(_cimg_instance
54051                                 "load_off(): Failed to read vertex %u/%u in file '%s'.",
54052                                 cimg_instance,
54053                                 l + 1,nb_points,filename?filename:"(FILE*)");
54054         }
54055         (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
54056       }
54057 
54058       // Read primitive data
54059       primitives.assign();
54060       colors.assign();
54061       bool stop_flag = false;
54062       while (!stop_flag) {
54063         float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
54064         unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
54065         *line = 0;
54066         if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
54067         else {
54068           ++nb_read;
54069           switch (prim) {
54070           case 1 : {
54071             if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) {
54072               cimg::warn(_cimg_instance
54073                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54074                          cimg_instance,
54075                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54076 
54077               err = std::fscanf(nfile,"%*[^\n] ");
54078             } else {
54079               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54080               CImg<tf>::vector(i0).move_to(primitives);
54081               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
54082             }
54083           } break;
54084           case 2 : {
54085             if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) {
54086               cimg::warn(_cimg_instance
54087                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54088                          cimg_instance,
54089                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54090 
54091               err = std::fscanf(nfile,"%*[^\n] ");
54092             } else {
54093               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54094               CImg<tf>::vector(i0,i1).move_to(primitives);
54095               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
54096             }
54097           } break;
54098           case 3 : {
54099             if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) {
54100               cimg::warn(_cimg_instance
54101                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54102                          cimg_instance,
54103                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54104 
54105               err = std::fscanf(nfile,"%*[^\n] ");
54106             } else {
54107               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54108               CImg<tf>::vector(i0,i2,i1).move_to(primitives);
54109               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
54110             }
54111           } break;
54112           case 4 : {
54113             if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) {
54114               cimg::warn(_cimg_instance
54115                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54116                          cimg_instance,
54117                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54118 
54119               err = std::fscanf(nfile,"%*[^\n] ");
54120             } else {
54121               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54122               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
54123               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
54124             }
54125           } break;
54126           case 5 : {
54127             if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) {
54128               cimg::warn(_cimg_instance
54129                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54130                          cimg_instance,
54131                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54132 
54133               err = std::fscanf(nfile,"%*[^\n] ");
54134             } else {
54135               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54136               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
54137               CImg<tf>::vector(i0,i4,i3).move_to(primitives);
54138               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
54139               ++nb_primitives;
54140             }
54141           } break;
54142           case 6 : {
54143             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) {
54144               cimg::warn(_cimg_instance
54145                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54146                          cimg_instance,
54147                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54148 
54149               err = std::fscanf(nfile,"%*[^\n] ");
54150             } else {
54151               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54152               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
54153               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
54154               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
54155               ++nb_primitives;
54156             }
54157           } break;
54158           case 7 : {
54159             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) {
54160               cimg::warn(_cimg_instance
54161                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54162                          cimg_instance,
54163                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54164 
54165               err = std::fscanf(nfile,"%*[^\n] ");
54166             } else {
54167               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54168               CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
54169               CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
54170               CImg<tf>::vector(i3,i2,i1).move_to(primitives);
54171               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
54172               ++(++nb_primitives);
54173             }
54174           } break;
54175           case 8 : {
54176             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) {
54177               cimg::warn(_cimg_instance
54178                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
54179                          cimg_instance,
54180                          nb_read,nb_primitives,filename?filename:"(FILE*)");
54181 
54182               err = std::fscanf(nfile,"%*[^\n] ");
54183             } else {
54184               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
54185               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
54186               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
54187               CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
54188               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
54189               ++(++nb_primitives);
54190             }
54191           } break;
54192           default :
54193             cimg::warn(_cimg_instance
54194                        "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
54195                        cimg_instance,
54196                        nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
54197 
54198             err = std::fscanf(nfile,"%*[^\n] ");
54199           }
54200         }
54201       }
54202       if (!file) cimg::fclose(nfile);
54203       if (primitives._width!=nb_primitives)
54204         cimg::warn(_cimg_instance
54205                    "load_off(): Only %u/%u primitives read from file '%s'.",
54206                    cimg_instance,
54207                    primitives._width,nb_primitives,filename?filename:"(FILE*)");
54208       return *this;
54209     }
54210 
54211     //! Load image sequence from a video file, using OpenCV library.
54212     /**
54213       \param filename Filename, as a C-string.
54214       \param first_frame Index of the first frame to read.
54215       \param last_frame Index of the last frame to read.
54216       \param step_frame Step value for frame reading.
54217       \param axis Alignment axis.
54218       \param align Appending alignment.
54219     **/
54220     CImg<T>& load_video(const char *const filename,
54221                         const unsigned int first_frame=0, const unsigned int last_frame=~0U,
54222                         const unsigned int step_frame=1,
54223                         const char axis='z', const float align=0) {
54224       return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this);
54225     }
54226 
54227     //! Load image sequence from a video file, using OpenCV library \newinstance.
54228     static CImg<T> get_load_video(const char *const filename,
54229                                   const unsigned int first_frame=0, const unsigned int last_frame=~0U,
54230                                   const unsigned int step_frame=1,
54231                                   const char axis='z', const float align=0) {
54232       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align);
54233     }
54234 
54235     //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
54236     /**
54237       \param filename Filename, as a C-string.
54238       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54239       \param align Appending alignment.
54240     **/
54241     CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
54242       return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
54243     }
54244 
54245     //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
54246     static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
54247       return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
54248     }
54249 
54250     //! Load gif file, using Imagemagick or GraphicsMagicks's external tools.
54251     /**
54252       \param filename Filename, as a C-string.
54253       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
54254       \param align Appending alignment.
54255     **/
54256     CImg<T>& load_gif_external(const char *const filename,
54257                                const char axis='z', const float align=0) {
54258       return get_load_gif_external(filename,axis,align).move_to(*this);
54259     }
54260 
54261     //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance.
54262     static CImg<T> get_load_gif_external(const char *const filename,
54263                                          const char axis='z', const float align=0) {
54264       return CImgList<T>().load_gif_external(filename).get_append(axis,align);
54265     }
54266 
54267     //! Load image from a HEIC file.
54268     /**
54269        \param filename Filename, as a C-string.
54270     **/
54271     CImg<T>& load_heif(const char *const filename) {
54272       return _load_heif(filename);
54273     }
54274 
54275     //! Load image from a HEIC file \newinstance.
54276     static CImg<T> get_load_heif(const char *const filename) {
54277       return CImg<T>().load_heif(filename);
54278     }
54279 
54280     CImg<T>& _load_heif(const char *const filename) {
54281 #ifndef cimg_use_heif
54282       return load_other(filename);
54283 #else
54284       try {
54285         heif::Context ctx;
54286         ctx.read_from_file(filename);
54287 
54288         heif::ImageHandle handle = ctx.get_primary_image_handle();
54289         const heif::Image image =
54290           handle.decode_image(heif_colorspace_RGB,handle.has_alpha_channel()?heif_chroma_interleaved_RGBA:
54291                               heif_chroma_interleaved_RGB);
54292         const int
54293           W = image.get_width(heif_channel_interleaved),
54294           H = image.get_height(heif_channel_interleaved),
54295           S = handle.has_alpha_channel()?4:3;
54296         assign(W,H,1,S);
54297 
54298         int stride;
54299         const unsigned char *const buffer = image.get_plane(heif_channel_interleaved,&stride);
54300         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;
54301         cimg_forY(*this,y) {
54302           const unsigned char *ptrs = buffer + y*stride;
54303           if (ptr_a) cimg_forX(*this,x) { // RGBA
54304               *(ptr_r++) = (T)*(ptrs++);
54305               *(ptr_g++) = (T)*(ptrs++);
54306               *(ptr_b++) = (T)*(ptrs++);
54307               *(ptr_a++) = (T)*(ptrs++);
54308             }
54309           else cimg_forX(*this,x) { // RGB
54310               *(ptr_r++) = (T)*(ptrs++);
54311               *(ptr_g++) = (T)*(ptrs++);
54312               *(ptr_b++) = (T)*(ptrs++);
54313             }
54314         }
54315       } catch (const heif::Error& e) {
54316         throw CImgInstanceException(_cimg_instance
54317                                     "load_heif(): Unable to decode image: %s",
54318                                     cimg_instance,
54319                                     e.get_message().c_str());
54320       } catch (...) {
54321         throw;
54322       }
54323       return *this;
54324 #endif
54325     }
54326 
54327     //! Load image using GraphicsMagick's external tool 'gm'.
54328     /**
54329        \param filename Filename, as a C-string.
54330     **/
54331     CImg<T>& load_graphicsmagick_external(const char *const filename) {
54332       if (!filename)
54333         throw CImgArgumentException(_cimg_instance
54334                                     "load_graphicsmagick_external(): Specified filename is (null).",
54335                                     cimg_instance);
54336       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
54337       CImg<charT> command(1024), filename_tmp(256);
54338       std::FILE *file = 0;
54339       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
54340 #if cimg_OS==1
54341       if (!cimg::system("which gm")) {
54342         cimg_snprintf(command,command._width,"%s convert \"%s\" pnm:-",
54343                       cimg::graphicsmagick_path(),s_filename.data());
54344         file = popen(command,"r");
54345         if (file) {
54346           const unsigned int omode = cimg::exception_mode();
54347           cimg::exception_mode(0);
54348           try { load_pnm(file); } catch (...) {
54349             pclose(file);
54350             cimg::exception_mode(omode);
54351             throw CImgIOException(_cimg_instance
54352                                   "load_graphicsmagick_external(): Failed to load file '%s' "
54353                                   "with external command 'gm'.",
54354                                   cimg_instance,
54355                                   filename);
54356           }
54357           pclose(file);
54358           return *this;
54359         }
54360       }
54361 #endif
54362       do {
54363         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm",
54364                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54365         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54366       } while (file);
54367       cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"",
54368                     cimg::graphicsmagick_path(),s_filename.data(),
54369                     CImg<charT>::string(filename_tmp)._system_strescape().data());
54370       cimg::system(command,cimg::graphicsmagick_path());
54371       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
54372         cimg::fclose(cimg::fopen(filename,"r"));
54373         throw CImgIOException(_cimg_instance
54374                               "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
54375                               cimg_instance,
54376                               filename);
54377 
54378       } else cimg::fclose(file);
54379       load_pnm(filename_tmp);
54380       std::remove(filename_tmp);
54381       return *this;
54382     }
54383 
54384     //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
54385     static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
54386       return CImg<T>().load_graphicsmagick_external(filename);
54387     }
54388 
54389     //! Load gzipped image file, using external tool 'gunzip'.
54390     /**
54391        \param filename Filename, as a C-string.
54392     **/
54393     CImg<T>& load_gzip_external(const char *const filename) {
54394       if (!filename)
54395         throw CImgIOException(_cimg_instance
54396                               "load_gzip_external(): Specified filename is (null).",
54397                               cimg_instance);
54398       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
54399       CImg<charT> command(1024), filename_tmp(256), body(256);
54400       const char
54401         *const ext = cimg::split_filename(filename,body),
54402         *const ext2 = cimg::split_filename(body,0);
54403 
54404       std::FILE *file = 0;
54405       do {
54406         if (!cimg::strcasecmp(ext,"gz")) {
54407           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54408                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
54409           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
54410                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54411         } else {
54412           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54413                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
54414           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
54415                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54416         }
54417         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54418       } while (file);
54419       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
54420                     cimg::gunzip_path(),
54421                     CImg<charT>::string(filename)._system_strescape().data(),
54422                     CImg<charT>::string(filename_tmp)._system_strescape().data());
54423       cimg::system(command);
54424       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
54425         cimg::fclose(cimg::fopen(filename,"r"));
54426         throw CImgIOException(_cimg_instance
54427                               "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
54428                               cimg_instance,
54429                               filename);
54430 
54431       } else cimg::fclose(file);
54432       load(filename_tmp);
54433       std::remove(filename_tmp);
54434       return *this;
54435     }
54436 
54437     //! Load gzipped image file, using external tool 'gunzip' \newinstance.
54438     static CImg<T> get_load_gzip_external(const char *const filename) {
54439       return CImg<T>().load_gzip_external(filename);
54440     }
54441 
54442     //! Load image using ImageMagick's external tool 'convert'.
54443     /**
54444        \param filename Filename, as a C-string.
54445     **/
54446     CImg<T>& load_imagemagick_external(const char *const filename) {
54447       if (!filename)
54448         throw CImgArgumentException(_cimg_instance
54449                                     "load_imagemagick_external(): Specified filename is (null).",
54450                                     cimg_instance);
54451       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
54452       CImg<charT> command(1024), filename_tmp(256);
54453       std::FILE *file = 0;
54454       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
54455 #if cimg_OS==1
54456       if (!cimg::system("which convert")) {
54457         cimg_snprintf(command,command._width,"%s%s \"%s\" pnm:-",
54458                       cimg::imagemagick_path(),
54459                       !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
54460                       s_filename.data());
54461         file = popen(command,"r");
54462         if (file) {
54463           const unsigned int omode = cimg::exception_mode();
54464           cimg::exception_mode(0);
54465           try { load_pnm(file); } catch (...) {
54466             pclose(file);
54467             cimg::exception_mode(omode);
54468             throw CImgIOException(_cimg_instance
54469                                   "load_imagemagick_external(): Failed to load file '%s' with "
54470                                   "external command 'magick/convert'.",
54471                                   cimg_instance,
54472                                   filename);
54473           }
54474           pclose(file);
54475           return *this;
54476         }
54477       }
54478 #endif
54479       do {
54480         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm",
54481                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54482         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54483       } while (file);
54484       cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"",
54485                     cimg::imagemagick_path(),
54486                     !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
54487                     s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
54488       cimg::system(command,cimg::imagemagick_path());
54489       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
54490         cimg::fclose(cimg::fopen(filename,"r"));
54491         throw CImgIOException(_cimg_instance
54492                               "load_imagemagick_external(): Failed to load file '%s' with "
54493                               "external command 'magick/convert'.",
54494                               cimg_instance,
54495                               filename);
54496 
54497       } else cimg::fclose(file);
54498       load_pnm(filename_tmp);
54499       std::remove(filename_tmp);
54500       return *this;
54501     }
54502 
54503     //! Load image using ImageMagick's external tool 'convert' \newinstance.
54504     static CImg<T> get_load_imagemagick_external(const char *const filename) {
54505       return CImg<T>().load_imagemagick_external(filename);
54506     }
54507 
54508     //! Load image from a DICOM file, using Medcon's external tool 'medcon'.
54509     /**
54510        \param filename Filename, as a C-string.
54511     **/
54512     CImg<T>& load_medcon_external(const char *const filename) {
54513       if (!filename)
54514         throw CImgArgumentException(_cimg_instance
54515                                     "load_medcon_external(): Specified filename is (null).",
54516                                     cimg_instance);
54517       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
54518       CImg<charT> command(1024), filename_tmp(256), body(256);
54519       cimg::fclose(cimg::fopen(filename,"r"));
54520       std::FILE *file = 0;
54521       do {
54522         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
54523         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54524       } while (file);
54525       cimg_snprintf(command,command._width,"\"%s\" -w -c anlz -o \"%s\" -f \"%s\"",
54526                     cimg::medcon_path(),
54527                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
54528                     CImg<charT>::string(filename)._system_strescape().data());
54529       cimg::system(command, cimg::medcon_path());
54530       cimg::split_filename(filename_tmp,body);
54531 
54532       cimg_snprintf(command,command._width,"%s.hdr",body._data);
54533       file = cimg::std_fopen(command,"rb");
54534       if (!file) {
54535         cimg_snprintf(command,command._width,"m000-%s.hdr",body._data);
54536         file = cimg::std_fopen(command,"rb");
54537         if (!file) {
54538           throw CImgIOException(_cimg_instance
54539                                 "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
54540                                 cimg_instance,
54541                                 filename);
54542         }
54543       }
54544       cimg::fclose(file);
54545       load_analyze(command);
54546       std::remove(command);
54547       cimg::split_filename(command,body);
54548       cimg_snprintf(command,command._width,"%s.img",body._data);
54549       std::remove(command);
54550       return *this;
54551     }
54552 
54553     //! Load image from a DICOM file, using Medcon's external tool 'medcon' \newinstance.
54554     static CImg<T> get_load_medcon_external(const char *const filename) {
54555       return CImg<T>().load_medcon_external(filename);
54556     }
54557 
54558     //! Load image from a .pdf file.
54559     /**
54560        \param filename Filename, as a C-string.
54561        \param resolution Image resolution.
54562     **/
54563     CImg<T>& load_pdf_external(const char *const filename, const unsigned int resolution=400) {
54564       if (!filename)
54565         throw CImgArgumentException(_cimg_instance
54566                                     "load_pdf_external(): Specified filename is (null).",
54567                                     cimg_instance);
54568       CImg<charT> command(1024), filename_tmp(256);
54569       std::FILE *file = 0;
54570       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
54571 #if cimg_OS==1
54572       cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o - -r%u \"%s\"",
54573                     resolution,s_filename.data());
54574       file = popen(command,"r");
54575       if (file) {
54576         const unsigned int omode = cimg::exception_mode();
54577         cimg::exception_mode(0);
54578         try { load_pnm(file); } catch (...) {
54579           pclose(file);
54580           cimg::exception_mode(omode);
54581           throw CImgIOException(_cimg_instance
54582                                 "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
54583                                 cimg_instance,
54584                                 filename);
54585         }
54586         pclose(file);
54587         return *this;
54588       }
54589 #endif
54590       do {
54591         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
54592                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54593         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54594       } while (file);
54595       cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o \"%s\" -r%u \"%s\"",
54596                     CImg<charT>::string(filename_tmp)._system_strescape().data(),resolution,s_filename.data());
54597       cimg::system(command,"gs");
54598       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
54599         cimg::fclose(cimg::fopen(filename,"r"));
54600         throw CImgIOException(_cimg_instance
54601                               "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
54602                               cimg_instance,
54603                               filename);
54604       } else cimg::fclose(file);
54605       load_pnm(filename_tmp);
54606       std::remove(filename_tmp);
54607       return *this;
54608     }
54609 
54610     //! Load image from a .pdf file \newinstance.
54611     static CImg<T> get_load_pdf_external(const char *const filename, const unsigned int resolution=400) {
54612       return CImg<T>().load_pdf_external(filename,resolution);
54613     }
54614 
54615     //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
54616     /**
54617        \param filename Filename, as a C-string.
54618     **/
54619     CImg<T>& load_dcraw_external(const char *const filename) {
54620       if (!filename)
54621         throw CImgArgumentException(_cimg_instance
54622                                     "load_dcraw_external(): Specified filename is (null).",
54623                                     cimg_instance);
54624       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
54625       CImg<charT> command(1024), filename_tmp(256);
54626       std::FILE *file = 0;
54627       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
54628 #if cimg_OS==1
54629       cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"",
54630                     cimg::dcraw_path(),s_filename.data());
54631       file = popen(command,"r");
54632       if (file) {
54633         const unsigned int omode = cimg::exception_mode();
54634         cimg::exception_mode(0);
54635         try { load_pnm(file); } catch (...) {
54636           pclose(file);
54637           cimg::exception_mode(omode);
54638           throw CImgIOException(_cimg_instance
54639                                 "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
54640                                 cimg_instance,
54641                                 filename);
54642         }
54643         pclose(file);
54644         return *this;
54645       }
54646 #endif
54647       do {
54648         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
54649                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54650         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54651       } while (file);
54652       cimg_snprintf(command,command._width,"\"%s\" -w -4 -c \"%s\" > \"%s\"",
54653                     cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
54654       cimg::system(command,cimg::dcraw_path());
54655       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
54656         cimg::fclose(cimg::fopen(filename,"r"));
54657         throw CImgIOException(_cimg_instance
54658                               "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
54659                               cimg_instance,
54660                               filename);
54661 
54662       } else cimg::fclose(file);
54663       load_pnm(filename_tmp);
54664       std::remove(filename_tmp);
54665       return *this;
54666     }
54667 
54668     //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
54669     static CImg<T> get_load_dcraw_external(const char *const filename) {
54670       return CImg<T>().load_dcraw_external(filename);
54671     }
54672 
54673 #ifdef cimg_use_opencv
54674 
54675     // Convert a continuous cv::Mat<uchar> to a CImg<uchar>.
54676     static CImg<ucharT> _cvmat2cimg(const cv::Mat &src) {
54677       if (src.channels()==1) return CImg<ucharT>(src.ptr(),src.cols,src.rows,1,1);
54678       else if (src.channels()==3) { // BGR
54679         CImg<ucharT> res(src.cols,src.rows,1,src.channels());
54680         const unsigned char *ptrs = src.ptr();
54681         unsigned char *pR = res.data(), *pG = res.data(0,0,0,1), *pB = res.data(0,0,0,2);
54682         cimg_forXY(res,x,y) { *(pB++) = *(ptrs++); *(pG++) = *(ptrs++); *(pR++) = *(ptrs++); }
54683         return res;
54684       }
54685       return CImg<ucharT>(src.ptr(),src.channels(),src.cols,src.rows,1,true).get_permute_axes("yzcx");
54686     }
54687 
54688     // Convert a CImg<T> to a cv::Mat.
54689     cv::Mat _cimg2cvmat() const {
54690       if (is_empty())
54691         throw CImgInstanceException(_cimg_instance
54692                                     "_cimg2cvmat() : Instance image is empty.",
54693                                     cimg_instance);
54694       if (_spectrum==2)
54695         throw CImgInstanceException(_cimg_instance
54696                                     "_cimg2cvmat() : Invalid number of channels (should be '1' or '3+').",
54697                                     cimg_instance);
54698       if (_depth!=1)
54699         throw CImgInstanceException(_cimg_instance
54700                                     "_cimg2cvmat() : Invalid number of slices (should be '1').",
54701                                     cimg_instance);
54702       int mat_type = -1;
54703       if (pixel_type()==cimg::type<unsigned char>::string()) mat_type = CV_8UC1;
54704       if (pixel_type()==cimg::type<char>::string()) mat_type = CV_8SC1;
54705       if (pixel_type()==cimg::type<unsigned short>::string()) mat_type = CV_16UC1;
54706       if (pixel_type()==cimg::type<short>::string()) mat_type = CV_16SC1;
54707       if (pixel_type()==cimg::type<int>::string()) mat_type = CV_32SC1;
54708       if (pixel_type()==cimg::type<float>::string()) mat_type = CV_32FC1;
54709       if (pixel_type()==cimg::type<double>::string()) mat_type = CV_64FC1;
54710       if (mat_type<0)
54711         throw CImgInstanceException(_cimg_instance
54712                                     "_cvmat2cimg() : pixel type '%s' is not supported.",
54713                                     cimg_instance,pixel_type());
54714       cv::Mat res;
54715       std::vector<cv::Mat> channels(_spectrum);
54716       if (_spectrum>1) {
54717         cimg_forC(*this,c)
54718           channels[c] = cv::Mat(_height,_width,mat_type,_data + _width*_height*(_spectrum - 1 - c));
54719         cv::merge(channels,res);
54720       } else res = cv::Mat(_height,_width,mat_type,_data).clone();
54721       return res;
54722     }
54723 
54724 #endif
54725 
54726     //! Load image from a camera stream, using OpenCV.
54727     /**
54728        \param index Index of the camera to capture images from (from 0 to 63).
54729        \param capture_width Width of the desired image ('0' stands for default value).
54730        \param capture_height Height of the desired image ('0' stands for default value).
54731        \param skip_frames Number of frames to skip before the capture.
54732        \param release_camera Tells if the camera resource must be released at the end of the method.
54733     **/
54734     CImg<T>& load_camera(const unsigned int camera_index=0,
54735                          const unsigned int capture_width=0, const unsigned int capture_height=0,
54736                          const unsigned int skip_frames=0, const bool release_camera=true) {
54737 #ifdef cimg_use_opencv
54738       if (camera_index>=64)
54739         throw CImgArgumentException(_cimg_instance
54740                                     "load_camera(): Invalid request for camera #%u "
54741                                     "(no more than 100 cameras can be managed simultaneously).",
54742                                     cimg_instance,
54743                                     camera_index);
54744       static cv::VideoCapture *captures[64] = { 0 };
54745       static unsigned int captures_w[64], captures_h[64];
54746       if (release_camera) {
54747         cimg::mutex(9);
54748         if (captures[camera_index]) captures[camera_index]->release();
54749         delete captures[camera_index];
54750         captures[camera_index] = 0;
54751         captures_w[camera_index] = captures_h[camera_index] = 0;
54752         cimg::mutex(9,0);
54753         return *this;
54754       }
54755       if (!captures[camera_index]) {
54756         cimg::mutex(9);
54757         captures[camera_index] = new cv::VideoCapture(camera_index);
54758         captures_w[camera_index] = captures_h[camera_index] = 0;
54759         if (!captures[camera_index]->isOpened()) {
54760           delete captures[camera_index];
54761           captures[camera_index] = 0;
54762           cimg::mutex(9,0);
54763           throw CImgIOException(_cimg_instance
54764                                 "load_camera(): Failed to initialize camera #%u.",
54765                                 cimg_instance,
54766                                 camera_index);
54767         }
54768         cimg::mutex(9,0);
54769       }
54770       cimg::mutex(9);
54771       if (capture_width!=captures_w[camera_index]) {
54772         captures[camera_index]->set(_cimg_cap_prop_frame_width,capture_width);
54773         captures_w[camera_index] = capture_width;
54774       }
54775       if (capture_height!=captures_h[camera_index]) {
54776         captures[camera_index]->set(_cimg_cap_prop_frame_height,capture_height);
54777         captures_h[camera_index] = capture_height;
54778       }
54779       for (unsigned int i = 0; i<skip_frames; ++i) captures[camera_index]->grab();
54780       cv::Mat cvimg;
54781       captures[camera_index]->read(cvimg);
54782       if (cvimg.empty()) assign(); else _cvmat2cimg(cvimg).move_to(*this);
54783       cimg::mutex(9,0);
54784       return *this;
54785 #else
54786       cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
54787       throw CImgIOException(_cimg_instance
54788                             "load_camera(): This function requires features from the OpenCV library "
54789                             "('-Dcimg_use_opencv' must be defined).",
54790                             cimg_instance);
54791 #endif
54792     }
54793 
54794     //! Load image from a camera stream, using OpenCV \newinstance.
54795     static CImg<T> get_load_camera(const unsigned int camera_index=0,
54796                                    const unsigned int capture_width=0, const unsigned int capture_height=0,
54797                                    const unsigned int skip_frames=0, const bool release_camera=true) {
54798       return CImg<T>().load_camera(camera_index,capture_width,capture_height,skip_frames,release_camera);
54799     }
54800 
54801     //! Load image using various non-native ways.
54802     /**
54803        \param filename Filename, as a C-string.
54804     **/
54805     CImg<T>& load_other(const char *const filename) {
54806       if (!filename)
54807         throw CImgArgumentException(_cimg_instance
54808                                     "load_other(): Specified filename is (null).",
54809                                     cimg_instance);
54810 
54811       const unsigned int omode = cimg::exception_mode();
54812       cimg::exception_mode(0);
54813       try { load_magick(filename); }
54814       catch (CImgException&) {
54815         try { load_imagemagick_external(filename); }
54816         catch (CImgException&) {
54817           try { load_graphicsmagick_external(filename); }
54818           catch (CImgException&) {
54819             try { load_cimg(filename); }
54820             catch (CImgException&) {
54821               try {
54822                 cimg::fclose(cimg::fopen(filename,"rb"));
54823               } catch (CImgException&) {
54824                 cimg::exception_mode(omode);
54825                 throw CImgIOException(_cimg_instance
54826                                       "load_other(): Failed to open file '%s'.",
54827                                       cimg_instance,
54828                                       filename);
54829               }
54830               cimg::exception_mode(omode);
54831               throw CImgIOException(_cimg_instance
54832                                     "load_other(): Failed to recognize format of file '%s'.",
54833                                     cimg_instance,
54834                                     filename);
54835             }
54836           }
54837         }
54838       }
54839       cimg::exception_mode(omode);
54840       return *this;
54841     }
54842 
54843     //! Load image using various non-native ways \newinstance.
54844     static CImg<T> get_load_other(const char *const filename) {
54845       return CImg<T>().load_other(filename);
54846     }
54847 
54848     //@}
54849     //---------------------------
54850     //
54851     //! \name Data Output
54852     //@{
54853     //---------------------------
54854 
54855     //! Display information about the image data.
54856     /**
54857        \param title Name for the considered image.
54858        \param display_stats Tells to compute and display image statistics.
54859     **/
54860     const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
54861 
54862       int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
54863       CImg<doubleT> st;
54864       if (!is_empty() && display_stats) {
54865         st = get_stats();
54866         xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
54867         xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
54868       }
54869 
54870       const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1,
54871         mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1;
54872 
54873       CImg<charT> _title(64);
54874       if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type());
54875 
54876       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
54877                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
54878                    cimg::t_bold,cimg::t_normal,(void*)this,
54879                    cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
54880                    (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))),
54881                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
54882                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
54883       if (_data)
54884         std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared");
54885       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
54886 
54887       if (!is_empty()) cimg_foroff(*this,off) {
54888         std::fprintf(cimg::output(),"%g",(double)_data[off]);
54889         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
54890         if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
54891       }
54892       if (!is_empty() && display_stats)
54893         std::fprintf(cimg::output(),
54894                      " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
54895                      "%scoords_max%s = (%u,%u,%u,%u).\n",
54896                      cimg::t_bold,cimg::t_normal,st[0],
54897                      cimg::t_bold,cimg::t_normal,st[1],
54898                      cimg::t_bold,cimg::t_normal,st[2],
54899                      cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
54900                      cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
54901                      cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
54902       else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
54903       std::fflush(cimg::output());
54904       return *this;
54905     }
54906 
54907     //! Display image into a CImgDisplay window.
54908     /**
54909        \param disp Display window.
54910     **/
54911     const CImg<T>& display(CImgDisplay& disp) const {
54912       disp.display(*this);
54913       return *this;
54914     }
54915 
54916     //! Display image into a CImgDisplay window, in an interactive way.
54917     /**
54918         \param disp Display window.
54919         \param display_info Tells if image information are displayed on the standard output.
54920         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
54921         \param exit_on_anykey Exit function when any key is pressed.
54922     **/
54923     const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0,
54924                            const bool exit_on_anykey=false) const {
54925       return _display(disp,0,display_info,XYZ,exit_on_anykey,false);
54926     }
54927 
54928     //! Display image into an interactive window.
54929     /**
54930         \param title Window title
54931         \param display_info Tells if image information are displayed on the standard output.
54932         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
54933         \param exit_on_anykey Exit function when any key is pressed.
54934     **/
54935     const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
54936                            const bool exit_on_anykey=false) const {
54937       CImgDisplay disp;
54938       return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
54939     }
54940 
54941     const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
54942                             unsigned int *const XYZ, const bool exit_on_anykey,
54943                             const bool exit_on_singleclick) const {
54944       unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0;
54945       int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1,
54946         old_mouse_x = -1, old_mouse_y = -1;
54947 
54948       if (!disp) {
54949         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
54950         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
54951         else disp.set_title("%s",title);
54952       } else if (title) disp.set_title("%s",title);
54953       disp.show().flush();
54954 
54955       const CImg<char> dtitle = CImg<char>::string(disp.title());
54956       if (display_info) print(dtitle);
54957 
54958       CImg<T> zoom;
54959       for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
54960         if (reset_view) {
54961           if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
54962           else {
54963             _XYZ[0] = (unsigned int)(x0 + x1 + 1)/2;
54964             _XYZ[1] = (unsigned int)(y0 + y1 + 1)/2;
54965             _XYZ[2] = (unsigned int)(z0 + z1 + 1)/2;
54966           }
54967           x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1;
54968           disp.resize(cimg_fitscreen(_width,_height,_depth),false);
54969           oldw = disp._width; oldh = disp._height;
54970           resize_disp = true;
54971           reset_view = false;
54972         }
54973         if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) {
54974           if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign();
54975         } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
54976 
54977         const CImg<T>& visu = zoom?zoom:*this;
54978         const unsigned int
54979           dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0,
54980           tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U);
54981         if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
54982           const float
54983             ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh,
54984             dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height());
54985           const unsigned int
54986             imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM);
54987           disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
54988           resize_disp = false;
54989         }
54990         oldw = tw; oldh = th;
54991 
54992         bool
54993           go_up = false, go_down = false, go_left = false, go_right = false,
54994           go_inc = false, go_dec = false, go_in = false, go_out = false,
54995           go_in_center = false;
54996 
54997         disp.set_title("%s",dtitle._data);
54998         if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
54999         if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
55000         if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
55001 
55002         disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y;
55003         CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true);
55004         old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y;
55005         is_first_select = false;
55006 
55007         if (disp.wheel()) {
55008           if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) &&
55009               (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) {
55010             go_left = !(go_right = disp.wheel()>0);
55011           } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) {
55012             go_down = !(go_up = disp.wheel()>0);
55013           } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55014             go_out = !(go_in = disp.wheel()>0); go_in_center = false;
55015           }
55016           disp.set_wheel();
55017         }
55018 
55019         const int
55020           sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
55021           sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
55022         if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
55023           x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
55024           x0+=sx0; y0+=sy0; z0+=sz0;
55025           if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) {
55026             if (exit_on_singleclick && (!zoom || is_empty())) break; else reset_view = true;
55027           }
55028           resize_disp = true;
55029         } else switch (key = disp.key()) {
55030 #if cimg_OS!=2
55031           case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
55032 #endif
55033           case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break;
55034           case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
55035               // Special mode: play stack of frames
55036               const unsigned int
55037                 w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)),
55038                 h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0));
55039               float frame_timing = 5;
55040               bool is_stopped = false;
55041               disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
55042               for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
55043                 if (disp.is_resized()) disp.resize(false);
55044                 if (!timer) {
55045                   visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
55046                   (++_XYZ[2])%=visu._depth;
55047                 }
55048                 if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
55049                 if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); }
55050                 switch (key = disp.key()) {
55051 #if cimg_OS!=2
55052                 case cimg::keyCTRLRIGHT :
55053 #endif
55054                 case cimg::keyCTRLLEFT : key = 0; break;
55055                 case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
55056                 case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
55057                 case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
55058                 case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
55059                 case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
55060                   (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break;
55061                 case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55062                     disp.set_fullscreen(false).
55063                       resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
55064                              CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
55065                     disp.set_key(key,false); key = 0;
55066                   } break;
55067                 case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55068                     disp.set_fullscreen(false).
55069                       resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
55070                   } break;
55071                 case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55072                     disp.set_fullscreen(false).
55073                       resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
55074                   } break;
55075                 case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55076                     disp.resize(disp.screen_width(),disp.screen_height(),false).
55077                       toggle_fullscreen().set_key(key,false); key = 0;
55078                   } break;
55079                 }
55080                 frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
55081                 disp.wait(20);
55082               }
55083               const unsigned int
55084                 w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
55085                 h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
55086               disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
55087               key = 0;
55088             } break;
55089           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
55090           case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
55091           case cimg::keyPADSUB : go_out = true; key = 0; break;
55092           case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
55093           case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
55094           case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
55095           case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
55096           case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
55097           case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
55098           case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
55099           case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
55100           case cimg::keyPAGEUP : go_inc = true; key = 0; break;
55101           case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
55102           }
55103         if (go_in) {
55104           const int
55105             mx = go_in_center?disp.width()/2:disp.mouse_x(),
55106             my = go_in_center?disp.height()/2:disp.mouse_y(),
55107             mX = mx*(width() + (depth()>1?depth():0))/disp.width(),
55108             mY = my*(height() + (depth()>1?depth():0))/disp.height();
55109           int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2];
55110           if (mX<width() && mY<height())  {
55111             X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height();
55112           }
55113           if (mX<width() && mY>=height()) {
55114             X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth();
55115           }
55116           if (mX>=width() && mY<height()) {
55117             Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth();
55118           }
55119           if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; }
55120           if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; }
55121           if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; }
55122         }
55123         if (go_out) {
55124           const int
55125             delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8,
55126             ndelta_x = delta_x?delta_x:(_width>1),
55127             ndelta_y = delta_y?delta_y:(_height>1),
55128             ndelta_z = delta_z?delta_z:(_depth>1);
55129           x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
55130           x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
55131           if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
55132           if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
55133           if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
55134           if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; }
55135           if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; }
55136           if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; }
55137           const float
55138             ratio = (float)(x1-x0)/(y1-y0),
55139             ratiow = (float)disp._width/disp._height,
55140             sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow));
55141           if (sub>0.01) resize_disp = true;
55142         }
55143         if (go_left) {
55144           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
55145           if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
55146           else { x1-=x0; x0 = 0; }
55147         }
55148         if (go_right) {
55149           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
55150           if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
55151           else { x0+=(width() - 1 - x1); x1 = width() - 1; }
55152         }
55153         if (go_up) {
55154           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
55155           if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; }
55156           else { y1-=y0; y0 = 0; }
55157         }
55158         if (go_down) {
55159           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
55160           if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
55161           else { y0+=(height() - 1 - y1); y1 = height() - 1; }
55162         }
55163         if (go_inc) {
55164           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
55165           if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; }
55166           else { z1-=z0; z0 = 0; }
55167         }
55168         if (go_dec) {
55169           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
55170           if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
55171           else { z0+=(depth() - 1 - z1); z1 = depth() - 1; }
55172         }
55173         disp.wait(100);
55174         if (!exit_on_anykey && key && key!=cimg::keyESC &&
55175             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
55176           key = 0;
55177         }
55178       }
55179       disp.set_key(key);
55180       if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
55181       return *this;
55182     }
55183 
55184     //! Display object 3D in an interactive window.
55185     /**
55186        \param disp Display window.
55187        \param vertices Vertices data of the 3D object.
55188        \param primitives Primitives data of the 3D object.
55189        \param colors Colors data of the 3D object.
55190        \param opacities Opacities data of the 3D object.
55191        \param centering Tells if the 3D object must be centered for the display.
55192        \param render_static Rendering mode.
55193        \param render_motion Rendering mode, when the 3D object is moved.
55194        \param is_double_sided Tells if the object primitives are double-sided.
55195        \param focale Focale
55196        \param light_x X-coordinate of the light source.
55197        \param light_y Y-coordinate of the light source.
55198        \param light_z Z-coordinate of the light source.
55199        \param specular_lightness Amount of specular light.
55200        \param specular_shininess Shininess of the object material.
55201        \param display_axes Tells if the 3D axes are displayed.
55202        \param pose_matrix Pointer to 12 values, defining a 3D pose (as a 4x3 matrix).
55203        \param exit_on_anykey Exit function when any key is pressed.
55204     **/
55205     template<typename tp, typename tf, typename tc, typename to>
55206     const CImg<T>& display_object3d(CImgDisplay& disp,
55207                                     const CImg<tp>& vertices,
55208                                     const CImgList<tf>& primitives,
55209                                     const CImgList<tc>& colors,
55210                                     const to& opacities,
55211                                     const bool centering=true,
55212                                     const int render_static=4, const int render_motion=1,
55213                                     const bool is_double_sided=true, const float focale=700,
55214                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55215                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55216                                     const bool display_axes=true, float *const pose_matrix=0,
55217                                     const bool exit_on_anykey=false) const {
55218       return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
55219                                render_motion,is_double_sided,focale,
55220                                light_x,light_y,light_z,specular_lightness,specular_shininess,
55221                                display_axes,pose_matrix,exit_on_anykey);
55222     }
55223 
55224     //! Display object 3D in an interactive window \simplification.
55225     template<typename tp, typename tf, typename tc, typename to>
55226     const CImg<T>& display_object3d(const char *const title,
55227                                     const CImg<tp>& vertices,
55228                                     const CImgList<tf>& primitives,
55229                                     const CImgList<tc>& colors,
55230                                     const to& opacities,
55231                                     const bool centering=true,
55232                                     const int render_static=4, const int render_motion=1,
55233                                     const bool is_double_sided=true, const float focale=700,
55234                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55235                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55236                                     const bool display_axes=true, float *const pose_matrix=0,
55237                                     const bool exit_on_anykey=false) const {
55238       CImgDisplay disp;
55239       return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
55240                                render_motion,is_double_sided,focale,
55241                                light_x,light_y,light_z,specular_lightness,specular_shininess,
55242                                display_axes,pose_matrix,exit_on_anykey);
55243     }
55244 
55245     //! Display object 3D in an interactive window \simplification.
55246     template<typename tp, typename tf, typename tc>
55247     const CImg<T>& display_object3d(CImgDisplay &disp,
55248                                     const CImg<tp>& vertices,
55249                                     const CImgList<tf>& primitives,
55250                                     const CImgList<tc>& colors,
55251                                     const bool centering=true,
55252                                     const int render_static=4, const int render_motion=1,
55253                                     const bool is_double_sided=true, const float focale=700,
55254                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55255                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55256                                     const bool display_axes=true, float *const pose_matrix=0,
55257                                     const bool exit_on_anykey=false) const {
55258       return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
55259                               render_static,render_motion,is_double_sided,focale,
55260                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55261                               display_axes,pose_matrix,exit_on_anykey);
55262     }
55263 
55264     //! Display object 3D in an interactive window \simplification.
55265     template<typename tp, typename tf, typename tc>
55266     const CImg<T>& display_object3d(const char *const title,
55267                                     const CImg<tp>& vertices,
55268                                     const CImgList<tf>& primitives,
55269                                     const CImgList<tc>& colors,
55270                                     const bool centering=true,
55271                                     const int render_static=4, const int render_motion=1,
55272                                     const bool is_double_sided=true, const float focale=700,
55273                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55274                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55275                                     const bool display_axes=true, float *const pose_matrix=0,
55276                                     const bool exit_on_anykey=false) const {
55277       return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
55278                               render_static,render_motion,is_double_sided,focale,
55279                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55280                               display_axes,pose_matrix,exit_on_anykey);
55281     }
55282 
55283     //! Display object 3D in an interactive window \simplification.
55284     template<typename tp, typename tf>
55285     const CImg<T>& display_object3d(CImgDisplay &disp,
55286                                     const CImg<tp>& vertices,
55287                                     const CImgList<tf>& primitives,
55288                                     const bool centering=true,
55289                                     const int render_static=4, const int render_motion=1,
55290                                     const bool is_double_sided=true, const float focale=700,
55291                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55292                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55293                                     const bool display_axes=true, float *const pose_matrix=0,
55294                                     const bool exit_on_anykey=false) const {
55295       return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
55296                               render_static,render_motion,is_double_sided,focale,
55297                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55298                               display_axes,pose_matrix,exit_on_anykey);
55299     }
55300 
55301 
55302     //! Display object 3D in an interactive window \simplification.
55303     template<typename tp, typename tf>
55304     const CImg<T>& display_object3d(const char *const title,
55305                                     const CImg<tp>& vertices,
55306                                     const CImgList<tf>& primitives,
55307                                     const bool centering=true,
55308                                     const int render_static=4, const int render_motion=1,
55309                                     const bool is_double_sided=true, const float focale=700,
55310                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55311                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55312                                     const bool display_axes=true, float *const pose_matrix=0,
55313                                     const bool exit_on_anykey=false) const {
55314       return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
55315                               render_static,render_motion,is_double_sided,focale,
55316                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55317                               display_axes,pose_matrix,exit_on_anykey);
55318     }
55319 
55320     //! Display object 3D in an interactive window \simplification.
55321     template<typename tp>
55322     const CImg<T>& display_object3d(CImgDisplay &disp,
55323                                     const CImg<tp>& vertices,
55324                                     const bool centering=true,
55325                                     const int render_static=4, const int render_motion=1,
55326                                     const bool is_double_sided=true, const float focale=700,
55327                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55328                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55329                                     const bool display_axes=true, float *const pose_matrix=0,
55330                                     const bool exit_on_anykey=false) const {
55331       return display_object3d(disp,vertices,CImgList<uintT>(),centering,
55332                               render_static,render_motion,is_double_sided,focale,
55333                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55334                               display_axes,pose_matrix,exit_on_anykey);
55335     }
55336 
55337     //! Display object 3D in an interactive window \simplification.
55338     template<typename tp>
55339     const CImg<T>& display_object3d(const char *const title,
55340                                     const CImg<tp>& vertices,
55341                                     const bool centering=true,
55342                                     const int render_static=4, const int render_motion=1,
55343                                     const bool is_double_sided=true, const float focale=700,
55344                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
55345                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
55346                                     const bool display_axes=true, float *const pose_matrix=0,
55347                                     const bool exit_on_anykey=false) const {
55348       return display_object3d(title,vertices,CImgList<uintT>(),centering,
55349                               render_static,render_motion,is_double_sided,focale,
55350                               light_x,light_y,light_z,specular_lightness,specular_shininess,
55351                               display_axes,pose_matrix,exit_on_anykey);
55352     }
55353 
55354     template<typename tp, typename tf, typename tc, typename to>
55355     const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
55356                                      const CImg<tp>& vertices,
55357                                      const CImgList<tf>& primitives,
55358                                      const CImgList<tc>& colors,
55359                                      const to& opacities,
55360                                      const bool centering,
55361                                      const int render_static, const int render_motion,
55362                                      const bool is_double_sided, const float focale,
55363                                      const float light_x, const float light_y, const float light_z,
55364                                      const float specular_lightness, const float specular_shininess,
55365                                      const bool display_axes, float *const pose_matrix,
55366                                      const bool exit_on_anykey) const {
55367       typedef typename cimg::superset<tp,float>::type tpfloat;
55368 
55369       // Check input arguments
55370       if (is_empty()) {
55371         CImg<T> background;
55372         if (colors && colors[0].size()==1) background.assign(1,2,1,1,64,128);
55373         else background.assign(1,2,1,3,32,64,32,116,64,96);
55374         if (disp) background.resize(disp.width(),disp.height(),1,-100,3);
55375         else background.resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
55376                                               CImgDisplay::screen_height()/2,1),1,-100,3);
55377         return background._display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
55378                                             render_static,render_motion,is_double_sided,focale,
55379                                             light_x,light_y,light_z,specular_lightness,specular_shininess,
55380                                             display_axes,pose_matrix,exit_on_anykey);
55381       } else { if (disp) disp.resize(*this,false); }
55382       CImg<charT> error_message(1024);
55383       if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
55384         throw CImgArgumentException(_cimg_instance
55385                                     "display_object3d(): Invalid specified 3D object (%u,%u) (%s).",
55386                                     cimg_instance,vertices._width,primitives._width,error_message.data());
55387       if (vertices._width && !primitives) {
55388         CImgList<tf> nprimitives(vertices._width,1,1,1,1);
55389         cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l;
55390         return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
55391                                  render_static,render_motion,is_double_sided,focale,
55392                                  light_x,light_y,light_z,specular_lightness,specular_shininess,
55393                                  display_axes,pose_matrix,exit_on_anykey);
55394       }
55395       if (!disp) {
55396         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
55397         if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
55398                                    pixel_type(),vertices._width,primitives._width);
55399       } else if (title) disp.set_title("%s",title);
55400 
55401       // Init 3D objects and compute object statistics
55402       CImg<floatT>
55403         pose,
55404         rotated_vertices(vertices._width,3),
55405         bbox_vertices, rotated_bbox_vertices,
55406         axes_vertices, rotated_axes_vertices,
55407         bbox_opacities, axes_opacities;
55408       CImgList<uintT> bbox_primitives, axes_primitives;
55409       CImgList<tf> reverse_primitives;
55410       CImgList<T> bbox_colors, bbox_colors2, axes_colors;
55411       unsigned int ns_width = 0, ns_height = 0;
55412       int _is_double_sided = (int)is_double_sided;
55413       bool ndisplay_axes = display_axes;
55414       const CImg<T>
55415         background_color(1,1,1,_spectrum,0),
55416         foreground_color(1,1,1,_spectrum,(T)std::min((int)cimg::type<T>::max(),255));
55417       float
55418         Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
55419         xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
55420         ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
55421         zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
55422       const float delta = cimg::max(xM - xm,yM - ym,zM - zm);
55423 
55424       rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
55425                                                    xm,xM,xM,xm,xm,xM,xM,xm,
55426                                                    ym,ym,yM,yM,ym,ym,yM,yM,
55427                                                    zm,zm,zm,zm,zM,zM,zM,zM);
55428       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);
55429       bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
55430       bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
55431       bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
55432 
55433       rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
55434                                                    0,20,0,0,22,-6,-6,
55435                                                    0,0,20,0,-6,22,-6,
55436                                                    0,0,0,20,0,0,22);
55437       axes_opacities.assign(3,1,1,1,1);
55438       axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
55439       axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
55440 
55441       // Begin user interaction loop
55442       CImg<T> visu0(*this,false), visu;
55443       CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
55444       bool init_pose = true, clicked = false, redraw = true;
55445       unsigned int key = 0, font_size = 32;
55446       int
55447         x0 = 0, y0 = 0, x1 = 0, y1 = 0,
55448         nrender_static = render_static,
55449         nrender_motion = render_motion;
55450       disp.show().flush();
55451 
55452       while (!disp.is_closed() && !key) {
55453 
55454         // Init object pose
55455         if (init_pose) {
55456           const float
55457             ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1,
55458             dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
55459           if (centering)
55460             CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
55461           else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
55462           if (pose_matrix) {
55463             CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
55464             pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
55465             pose0(3,3) = pose(3,3) = 1;
55466             (pose0*pose).get_crop(0,0,3,2).move_to(pose);
55467             Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
55468           } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
55469           init_pose = false;
55470           redraw = true;
55471         }
55472 
55473         // Rotate and draw 3D object
55474         if (redraw) {
55475           const float
55476             r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
55477             r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
55478             r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
55479           if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) {
55480             const tp *const pv0 = vertices.data(), *const pv1 = vertices.data(0,1), *const pv2 = vertices.data(0,2);
55481             float
55482               *const prv0 = rotated_vertices.data(),
55483               *const prv1 = rotated_vertices.data(0,1),
55484               *const prv2 = rotated_vertices.data(0,2);
55485             cimg_pragma_openmp(parallel for cimg_openmp_if(vertices.width()>(cimg_openmp_sizefactor)*1024))
55486             cimg_forX(vertices,l) {
55487               const float x = (float)pv0[l], y = (float)pv1[l], z = (float)pv2[l];
55488               prv0[l] = r00*x + r10*y + r20*z + r30;
55489               prv1[l] = r01*x + r11*y + r21*z + r31;
55490               prv2[l] = r02*x + r12*y + r22*z + r32;
55491             }
55492           }
55493           else cimg_forX(bbox_vertices,l) {
55494               const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
55495               rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
55496               rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
55497               rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
55498             }
55499 
55500           // Draw objects
55501           const bool render_with_zbuffer = !clicked && nrender_static>0;
55502           visu = visu0;
55503           if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
55504             visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
55505                                rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
55506               draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
55507                             rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
55508           else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
55509                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
55510                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
55511                                    colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
55512                                    width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff,
55513                                    specular_lightness,specular_shininess,1,sprite_scale);
55514           // Draw axes
55515           if (ndisplay_axes) {
55516             const float
55517               n = 1e-8f + cimg::hypot(r00,r01,r02),
55518               _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
55519               _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
55520               _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
55521               Xaxes = 25, Yaxes = visu._height - 38.f;
55522             cimg_forX(axes_vertices,l) {
55523               const float
55524                 x = axes_vertices(l,0),
55525                 y = axes_vertices(l,1),
55526                 z = axes_vertices(l,2);
55527               rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
55528               rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
55529               rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
55530             }
55531             axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f;
55532             axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f;
55533             axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f;
55534             visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
55535                                axes_colors,axes_opacities,1,false,focale).
55536               draw_text((int)(Xaxes + rotated_axes_vertices(4,0)),
55537                         (int)(Yaxes + rotated_axes_vertices(4,1)),
55538                         "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
55539               draw_text((int)(Xaxes + rotated_axes_vertices(5,0)),
55540                         (int)(Yaxes + rotated_axes_vertices(5,1)),
55541                         "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
55542               draw_text((int)(Xaxes + rotated_axes_vertices(6,0)),
55543                         (int)(Yaxes + rotated_axes_vertices(6,1)),
55544                         "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
55545           }
55546           visu.display(disp);
55547           if (!clicked || nrender_motion==nrender_static) redraw = false;
55548         }
55549 
55550         // Handle user interaction
55551         if (!redraw) disp.wait();
55552         if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
55553           redraw = true;
55554           if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
55555           else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
55556           const bool is_keyCTRL = disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT();
55557           if (disp.button()&1 && !is_keyCTRL) {
55558             const float
55559               R = 0.45f*std::min(disp.width(),disp.height()),
55560               R2 = R*R,
55561               u0 = (float)(x0 - disp.width()/2),
55562               v0 = (float)(y0 - disp.height()/2),
55563               u1 = (float)(x1 - disp.width()/2),
55564               v1 = (float)(y1 - disp.height()/2),
55565               n0 = cimg::hypot(u0,v0),
55566               n1 = cimg::hypot(u1,v1),
55567               nu0 = n0>R?(u0*R/n0):u0,
55568               nv0 = n0>R?(v0*R/n0):v0,
55569               nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
55570               nu1 = n1>R?(u1*R/n1):u1,
55571               nv1 = n1>R?(v1*R/n1):v1,
55572               nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
55573               u = nv0*nw1 - nw0*nv1,
55574               v = nw0*nu1 - nu0*nw1,
55575               w = nv0*nu1 - nu0*nv1,
55576               n = cimg::hypot(u,v,w),
55577               alpha = (float)std::asin(n/R2)*180/cimg::PI;
55578             (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose);
55579             x0 = x1; y0 = y1;
55580           }
55581           if (disp.button()&2 && !is_keyCTRL) {
55582             if (focale>0) Zoff-=(y0 - y1)*focale/400;
55583             else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; }
55584             x0 = x1; y0 = y1;
55585           }
55586           if (disp.wheel()) {
55587             if (focale>0) Zoff-=disp.wheel()*focale/20;
55588             else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; }
55589             disp.set_wheel();
55590           }
55591           if (disp.button()&4 || (disp.button()&1 && is_keyCTRL)) {
55592             Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1;
55593           }
55594           if ((disp.button()&1) && (disp.button()&2) && !is_keyCTRL) {
55595             init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
55596             pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
55597           }
55598         } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
55599 
55600         CImg<charT> filename(32);
55601         switch (key = disp.key()) {
55602 #if cimg_OS!=2
55603         case cimg::keyCTRLRIGHT :
55604 #endif
55605         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
55606         case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55607             disp.set_fullscreen(false).
55608               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
55609                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
55610               _is_resized = true;
55611             disp.set_key(key,false); key = 0;
55612           } break;
55613         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55614             disp.set_fullscreen(false).
55615               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
55616             disp.set_key(key,false); key = 0;
55617           } break;
55618         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55619             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
55620             disp.set_key(key,false); key = 0;
55621           } break;
55622         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55623             if (!ns_width || !ns_height ||
55624                 ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
55625               ns_width = disp.screen_width()*3U/4;
55626               ns_height = disp.screen_height()*3U/4;
55627             }
55628             if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
55629             else {
55630               ns_width = disp._width; ns_height = disp._height;
55631               disp.resize(disp.screen_width(),disp.screen_height(),false);
55632             }
55633             disp.toggle_fullscreen()._is_resized = true;
55634             disp.set_key(key,false); key = 0;
55635           } break;
55636         case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55637             // Switch single/double-sided primitives.
55638             if (--_is_double_sided==-2) _is_double_sided = 1;
55639             if (_is_double_sided>=0) reverse_primitives.assign();
55640             else primitives.get_reverse_object3d().move_to(reverse_primitives);
55641             disp.set_key(key,false); key = 0; redraw = true;
55642           } break;
55643         case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
55644             if (zbuffer) zbuffer.assign();
55645             else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
55646             disp.set_key(key,false); key = 0; redraw = true;
55647           } break;
55648         case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes
55649             ndisplay_axes = !ndisplay_axes;
55650             disp.set_key(key,false); key = 0; redraw = true;
55651           } break;
55652         case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points
55653             nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
55654             disp.set_key(key,false); key = 0; redraw = true;
55655           } break;
55656         case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines
55657             nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
55658             disp.set_key(key,false); key = 0; redraw = true;
55659           } break;
55660         case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat
55661             nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
55662             disp.set_key(key,false); key = 0; redraw = true;
55663           } break;
55664         case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded
55665             nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
55666             disp.set_key(key,false); key = 0; redraw = true;
55667           } break;
55668         case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55669             // Set rendering mode to gouraud-shaded.
55670             nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
55671             disp.set_key(key,false); key = 0; redraw = true;
55672           } break;
55673         case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded
55674             nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
55675             disp.set_key(key,false); key = 0; redraw = true;
55676           } break;
55677         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
55678             static unsigned int snap_number = 0;
55679             std::FILE *file;
55680             do {
55681               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
55682               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
55683             } while (file);
55684             (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
55685             visu.save(filename);
55686             (+visu).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
55687             disp.set_key(key,false); key = 0;
55688           } break;
55689         case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
55690             static unsigned int snap_number = 0;
55691             std::FILE *file;
55692             do {
55693               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++);
55694               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
55695             } while (file);
55696             (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
55697             vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
55698             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
55699             disp.set_key(key,false); key = 0;
55700           } break;
55701         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
55702             static unsigned int snap_number = 0;
55703             std::FILE *file;
55704             do {
55705 
55706 #ifdef cimg_use_zlib
55707               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
55708 #else
55709               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
55710 #endif
55711               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
55712             } while (file);
55713             (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
55714             vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
55715               save(filename);
55716             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
55717             disp.set_key(key,false); key = 0;
55718           } break;
55719 
55720 #ifdef cimg_use_board
55721         case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
55722             static unsigned int snap_number = 0;
55723             std::FILE *file;
55724             do {
55725               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++);
55726               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
55727             } while (file);
55728             (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp);
55729             LibBoard::Board board;
55730             (+visu)._draw_object3d(&board,zbuffer.fill(0),
55731                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
55732                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
55733                                    colors,opacities,clicked?nrender_motion:nrender_static,
55734                                    _is_double_sided==1,focale,
55735                                    visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
55736                                    specular_lightness,specular_shininess,1,
55737                                    sprite_scale);
55738             board.saveEPS(filename);
55739             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
55740             disp.set_key(key,false); key = 0;
55741           } break;
55742         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
55743             static unsigned int snap_number = 0;
55744             std::FILE *file;
55745             do {
55746               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++);
55747               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
55748             } while (file);
55749             (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp);
55750             LibBoard::Board board;
55751             (+visu)._draw_object3d(&board,zbuffer.fill(0),
55752                                    Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
55753                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
55754                                    colors,opacities,clicked?nrender_motion:nrender_static,
55755                                    _is_double_sided==1,focale,
55756                                    visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
55757                                    specular_lightness,specular_shininess,1,
55758                                    sprite_scale);
55759             board.saveSVG(filename);
55760             (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
55761             disp.set_key(key,false); key = 0;
55762           } break;
55763 #endif
55764         }
55765         if (disp.is_resized()) {
55766           disp.resize(false); visu0 = get_resize(disp,1);
55767           if (zbuffer) zbuffer.assign(disp.width(),disp.height());
55768           redraw = true;
55769         }
55770         if (!exit_on_anykey && key && key!=cimg::keyESC &&
55771             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
55772           key = 0;
55773         }
55774       }
55775       if (pose_matrix) {
55776         std::memcpy(pose_matrix,pose._data,12*sizeof(float));
55777         pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
55778       }
55779       disp.set_button().set_key(key);
55780       return *this;
55781     }
55782 
55783     //! Display 1D graph in an interactive window.
55784     /**
55785        \param disp Display window.
55786        \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
55787        \param vertex_type Vertex type.
55788        \param labelx Title for the horizontal axis, as a C-string.
55789        \param xmin Minimum value along the X-axis.
55790        \param xmax Maximum value along the X-axis.
55791        \param labely Title for the vertical axis, as a C-string.
55792        \param ymin Minimum value along the X-axis.
55793        \param ymax Maximum value along the X-axis.
55794        \param exit_on_anykey Exit function when any key is pressed.
55795     **/
55796     const CImg<T>& display_graph(CImgDisplay &disp,
55797                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
55798                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
55799                                  const char *const labely=0, const double ymin=0, const double ymax=0,
55800                                  const bool exit_on_anykey=false) const {
55801       return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
55802     }
55803 
55804     //! Display 1D graph in an interactive window \overloading.
55805     const CImg<T>& display_graph(const char *const title=0,
55806                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
55807                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
55808                                  const char *const labely=0, const double ymin=0, const double ymax=0,
55809                                  const bool exit_on_anykey=false) const {
55810       CImgDisplay disp;
55811       return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
55812     }
55813 
55814     const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
55815                                   const unsigned int plot_type=1, const unsigned int vertex_type=1,
55816                                   const char *const labelx=0, const double xmin=0, const double xmax=0,
55817                                   const char *const labely=0, const double ymin=0, const double ymax=0,
55818                                   const bool exit_on_anykey=false) const {
55819       if (is_empty())
55820         throw CImgInstanceException(_cimg_instance
55821                                     "display_graph(): Empty instance.",
55822                                     cimg_instance);
55823       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
55824                    set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
55825       const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1);
55826       const unsigned int old_normalization = disp.normalization();
55827       disp.show().flush()._normalization = 0;
55828 
55829       double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
55830       if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
55831       int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
55832 
55833       for (bool reset_view = true; !key && !disp.is_closed(); ) {
55834         if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; }
55835         CImg<T> zoom(x1 - x0 + 1,1,1,spectrum());
55836         cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true);
55837         if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
55838         if (y0==y1) { --y0; ++y1; }
55839 
55840         const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
55841                                                            labelx,
55842                                                            nxmin + x0*(nxmax - nxmin)/siz1,
55843                                                            nxmin + x1*(nxmax - nxmin)/siz1,
55844                                                            labely,y0,y1,true);
55845         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
55846         if (selection[0]>=0) {
55847           if (selection[2]<0) reset_view = true;
55848           else {
55849             x1 = x0 + selection[2]; x0+=selection[0];
55850             if (selection[1]>=0 && selection[3]>=0) {
55851               y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32);
55852               y1-=selection[1]*(y1 - y0)/(disp.height() - 32);
55853             }
55854           }
55855         } else {
55856           bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
55857           switch (key = (int)disp.key()) {
55858           case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
55859           case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
55860           case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
55861           case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
55862             break;
55863           case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
55864             break;
55865           case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
55866           case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
55867           case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
55868           case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
55869           case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
55870           case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
55871           }
55872           if (disp.wheel()) {
55873             if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0);
55874             else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
55875             else go_out = !(go_in = disp.wheel()>0);
55876             key = 0;
55877           }
55878 
55879           if (go_in) {
55880             const int
55881               xsiz = x1 - x0,
55882               mx = (mouse_x - 16)*xsiz/(disp.width() - 32),
55883               cx = x0 + cimg::cut(mx,0,xsiz);
55884             if (x1 - x0>4) {
55885               x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8;
55886               if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
55887                 const double
55888                   ysiz = y1 - y0,
55889                   my = (mouse_y - 16)*ysiz/(disp.height() - 32),
55890                   cy = y1 - cimg::cut(my,0.,ysiz);
55891                 y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8;
55892               } else y0 = y1 = 0;
55893             }
55894           }
55895           if (go_out) {
55896             if (x0>0 || x1<(int)siz1) {
55897               const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1);
55898               const double ndelta_y = (y1 - y0)/8;
55899               x0-=ndelta_x; x1+=ndelta_x;
55900               y0-=ndelta_y; y1+=ndelta_y;
55901               if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
55902               if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
55903             }
55904           }
55905           if (go_left) {
55906             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
55907             if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
55908             else { x1-=x0; x0 = 0; }
55909             go_left = false;
55910           }
55911           if (go_right) {
55912             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
55913             if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
55914             else { x0+=(siz1 - x1); x1 = (int)siz1; }
55915             go_right = false;
55916           }
55917           if (go_up) {
55918             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
55919             y0+=ndelta; y1+=ndelta;
55920             go_up = false;
55921           }
55922           if (go_down) {
55923             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
55924             y0-=ndelta; y1-=ndelta;
55925             go_down = false;
55926           }
55927         }
55928         if (!exit_on_anykey && key && key!=(int)cimg::keyESC &&
55929             (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
55930           disp.set_key(key,false);
55931           key = 0;
55932         }
55933       }
55934       disp._normalization = old_normalization;
55935       return *this;
55936     }
55937 
55938     //! Save image as a file.
55939     /**
55940        \param filename Filename, as a C-string.
55941        \param number When positive, represents an index added to the filename. Otherwise, no number is added.
55942        \param digits Number of digits used for adding the number to the filename.
55943        \note
55944        - The used file format is defined by the file extension in the filename \p filename.
55945        - Parameter \p number can be used to add a 6-digit number to the filename before saving.
55946 
55947     **/
55948     const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
55949       if (!filename)
55950         throw CImgArgumentException(_cimg_instance
55951                                     "save(): Specified filename is (null).",
55952                                     cimg_instance);
55953       // Do not test for empty instances, since .cimg format is able to manage empty instances.
55954       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
55955       const char *const ext = cimg::split_filename(filename);
55956       CImg<charT> nfilename(1024);
55957       const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
55958         filename;
55959 
55960 #ifdef cimg_save_plugin
55961       cimg_save_plugin(fn);
55962 #endif
55963 #ifdef cimg_save_plugin1
55964       cimg_save_plugin1(fn);
55965 #endif
55966 #ifdef cimg_save_plugin2
55967       cimg_save_plugin2(fn);
55968 #endif
55969 #ifdef cimg_save_plugin3
55970       cimg_save_plugin3(fn);
55971 #endif
55972 #ifdef cimg_save_plugin4
55973       cimg_save_plugin4(fn);
55974 #endif
55975 #ifdef cimg_save_plugin5
55976       cimg_save_plugin5(fn);
55977 #endif
55978 #ifdef cimg_save_plugin6
55979       cimg_save_plugin6(fn);
55980 #endif
55981 #ifdef cimg_save_plugin7
55982       cimg_save_plugin7(fn);
55983 #endif
55984 #ifdef cimg_save_plugin8
55985       cimg_save_plugin8(fn);
55986 #endif
55987       // Text formats
55988       if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
55989       else if (!cimg::strcasecmp(ext,"csv") ||
55990                !cimg::strcasecmp(ext,"dlm") ||
55991                !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
55992       else if (!cimg::strcasecmp(ext,"cpp") ||
55993                !cimg::strcasecmp(ext,"hpp") ||
55994                !cimg::strcasecmp(ext,"h") ||
55995                !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
55996 
55997       // 2D binary formats
55998       else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
55999       else if (!cimg::strcasecmp(ext,"jpg") ||
56000                !cimg::strcasecmp(ext,"jpeg") ||
56001                !cimg::strcasecmp(ext,"jpe") ||
56002                !cimg::strcasecmp(ext,"jfif") ||
56003                !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
56004       else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
56005       else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
56006       else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
56007       else if (!cimg::strcasecmp(ext,"pgm") ||
56008                !cimg::strcasecmp(ext,"ppm") ||
56009                !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
56010       else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
56011       else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
56012       else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
56013       else if (!cimg::strcasecmp(ext,"tif") ||
56014                !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
56015 
56016       // 3D binary formats
56017       else if (!*ext) {
56018 #ifdef cimg_use_zlib
56019         return save_cimg(fn,true);
56020 #else
56021         return save_cimg(fn,false);
56022 #endif
56023       } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
56024       else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false);
56025       else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
56026       else if (!cimg::strcasecmp(ext,"hdr") ||
56027                !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
56028       else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
56029       else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
56030       else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
56031       else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
56032 
56033       // Archive files
56034       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
56035 
56036       // Image sequences
56037       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
56038       else if (!cimg::strcasecmp(ext,"avi") ||
56039                !cimg::strcasecmp(ext,"mov") ||
56040                !cimg::strcasecmp(ext,"asf") ||
56041                !cimg::strcasecmp(ext,"divx") ||
56042                !cimg::strcasecmp(ext,"flv") ||
56043                !cimg::strcasecmp(ext,"mpg") ||
56044                !cimg::strcasecmp(ext,"m1v") ||
56045                !cimg::strcasecmp(ext,"m2v") ||
56046                !cimg::strcasecmp(ext,"m4v") ||
56047                !cimg::strcasecmp(ext,"mjp") ||
56048                !cimg::strcasecmp(ext,"mp4") ||
56049                !cimg::strcasecmp(ext,"mkv") ||
56050                !cimg::strcasecmp(ext,"mpe") ||
56051                !cimg::strcasecmp(ext,"movie") ||
56052                !cimg::strcasecmp(ext,"ogm") ||
56053                !cimg::strcasecmp(ext,"ogg") ||
56054                !cimg::strcasecmp(ext,"ogv") ||
56055                !cimg::strcasecmp(ext,"qt") ||
56056                !cimg::strcasecmp(ext,"rm") ||
56057                !cimg::strcasecmp(ext,"vob") ||
56058                !cimg::strcasecmp(ext,"webm") ||
56059                !cimg::strcasecmp(ext,"wmv") ||
56060                !cimg::strcasecmp(ext,"xvid") ||
56061                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
56062       return save_other(fn);
56063     }
56064 
56065     //! Save image as an ascii file.
56066     /**
56067       \param filename Filename, as a C-string.
56068     **/
56069     const CImg<T>& save_ascii(const char *const filename) const {
56070       return _save_ascii(0,filename);
56071     }
56072 
56073     //! Save image as an Ascii file \overloading.
56074     const CImg<T>& save_ascii(std::FILE *const file) const {
56075       return _save_ascii(file,0);
56076     }
56077 
56078     const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
56079       if (!file && !filename)
56080         throw CImgArgumentException(_cimg_instance
56081                                     "save_ascii(): Specified filename is (null).",
56082                                     cimg_instance);
56083       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
56084       std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
56085       const T* ptrs = _data;
56086       cimg_forYZC(*this,y,z,c) {
56087         cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++));
56088         std::fputc('\n',nfile);
56089       }
56090       if (!file) cimg::fclose(nfile);
56091       return *this;
56092     }
56093 
56094     //! Save image as a .cpp source file.
56095     /**
56096       \param filename Filename, as a C-string.
56097     **/
56098     const CImg<T>& save_cpp(const char *const filename) const {
56099       return _save_cpp(0,filename);
56100     }
56101 
56102     //! Save image as a .cpp source file \overloading.
56103     const CImg<T>& save_cpp(std::FILE *const file) const {
56104       return _save_cpp(file,0);
56105     }
56106 
56107     const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
56108       if (!file && !filename)
56109         throw CImgArgumentException(_cimg_instance
56110                                     "save_cpp(): Specified filename is (null).",
56111                                     cimg_instance);
56112       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
56113       CImg<charT> varname(1024); *varname = 0;
56114       if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data);
56115       if (!*varname) cimg_snprintf(varname,varname._width,"unnamed");
56116       std::fprintf(nfile,
56117                    "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
56118                    "%s data_%s[] = { %s\n  ",
56119                    varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data,
56120                    is_empty()?"};":"");
56121       if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) {
56122         std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
56123         if (off==siz) std::fprintf(nfile," };\n");
56124         else if (!((off + 1)%16)) std::fprintf(nfile,",\n  ");
56125         else std::fprintf(nfile,", ");
56126       }
56127       if (!file) cimg::fclose(nfile);
56128       return *this;
56129     }
56130 
56131     //! Save image as a DLM file.
56132     /**
56133        \param filename Filename, as a C-string.
56134     **/
56135     const CImg<T>& save_dlm(const char *const filename) const {
56136       return _save_dlm(0,filename);
56137     }
56138 
56139     //! Save image as a DLM file \overloading.
56140     const CImg<T>& save_dlm(std::FILE *const file) const {
56141       return _save_dlm(file,0);
56142     }
56143 
56144     const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
56145       if (!file && !filename)
56146         throw CImgArgumentException(_cimg_instance
56147                                     "save_dlm(): Specified filename is (null).",
56148                                     cimg_instance);
56149       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56150       if (_depth>1)
56151         cimg::warn(_cimg_instance
56152                    "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
56153                    cimg_instance,
56154                    filename?filename:"(FILE*)");
56155       if (_spectrum>1)
56156         cimg::warn(_cimg_instance
56157                    "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
56158                    cimg_instance,
56159                    filename?filename:"(FILE*)");
56160 
56161       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
56162       const T* ptrs = _data;
56163       cimg_forYZC(*this,y,z,c) {
56164         cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":",");
56165         std::fputc('\n',nfile);
56166       }
56167       if (!file) cimg::fclose(nfile);
56168       return *this;
56169     }
56170 
56171     //! Save image as a BMP file.
56172     /**
56173       \param filename Filename, as a C-string.
56174     **/
56175     const CImg<T>& save_bmp(const char *const filename) const {
56176       return _save_bmp(0,filename);
56177     }
56178 
56179     //! Save image as a BMP file \overloading.
56180     const CImg<T>& save_bmp(std::FILE *const file) const {
56181       return _save_bmp(file,0);
56182     }
56183 
56184     const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
56185       if (!file && !filename)
56186         throw CImgArgumentException(_cimg_instance
56187                                     "save_bmp(): Specified filename is (null).",
56188                                     cimg_instance);
56189       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56190       if (_depth>1)
56191         cimg::warn(_cimg_instance
56192                    "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56193                    cimg_instance,
56194                    filename?filename:"(FILE*)");
56195       if (_spectrum>3)
56196         cimg::warn(_cimg_instance
56197                    "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
56198                    cimg_instance,
56199                    filename?filename:"(FILE*)");
56200 
56201       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
56202       CImg<ucharT> header(54,1,1,1,0);
56203       unsigned char align_buf[4] = { 0 };
56204       const unsigned int
56205         align = (4 - (3*_width)%4)%4,
56206         buf_size = (3*_width + align)*height(),
56207         file_size = 54 + buf_size;
56208       header[0] = 'B'; header[1] = 'M';
56209       header[0x02] = file_size&0xFF;
56210       header[0x03] = (file_size>>8)&0xFF;
56211       header[0x04] = (file_size>>16)&0xFF;
56212       header[0x05] = (file_size>>24)&0xFF;
56213       header[0x0A] = 0x36;
56214       header[0x0E] = 0x28;
56215       header[0x12] = _width&0xFF;
56216       header[0x13] = (_width>>8)&0xFF;
56217       header[0x14] = (_width>>16)&0xFF;
56218       header[0x15] = (_width>>24)&0xFF;
56219       header[0x16] = _height&0xFF;
56220       header[0x17] = (_height>>8)&0xFF;
56221       header[0x18] = (_height>>16)&0xFF;
56222       header[0x19] = (_height>>24)&0xFF;
56223       header[0x1A] = 1;
56224       header[0x1B] = 0;
56225       header[0x1C] = 24;
56226       header[0x1D] = 0;
56227       header[0x22] = buf_size&0xFF;
56228       header[0x23] = (buf_size>>8)&0xFF;
56229       header[0x24] = (buf_size>>16)&0xFF;
56230       header[0x25] = (buf_size>>24)&0xFF;
56231       header[0x27] = 0x1;
56232       header[0x2B] = 0x1;
56233       cimg::fwrite(header._data,54,nfile);
56234 
56235       const T
56236         *ptr_r = data(0,_height - 1,0,0),
56237         *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0,
56238         *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0;
56239 
56240       switch (_spectrum) {
56241       case 1 : {
56242         cimg_forY(*this,y) {
56243           cimg_forX(*this,x) {
56244             const unsigned char val = (unsigned char)*(ptr_r++);
56245             std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
56246           }
56247           cimg::fwrite(align_buf,align,nfile);
56248           ptr_r-=2*_width;
56249         }
56250       } break;
56251       case 2 : {
56252         cimg_forY(*this,y) {
56253           cimg_forX(*this,x) {
56254             std::fputc(0,nfile);
56255             std::fputc((unsigned char)(*(ptr_g++)),nfile);
56256             std::fputc((unsigned char)(*(ptr_r++)),nfile);
56257           }
56258           cimg::fwrite(align_buf,align,nfile);
56259           ptr_r-=2*_width; ptr_g-=2*_width;
56260         }
56261       } break;
56262       default : {
56263         cimg_forY(*this,y) {
56264           cimg_forX(*this,x) {
56265             std::fputc((unsigned char)(*(ptr_b++)),nfile);
56266             std::fputc((unsigned char)(*(ptr_g++)),nfile);
56267             std::fputc((unsigned char)(*(ptr_r++)),nfile);
56268           }
56269           cimg::fwrite(align_buf,align,nfile);
56270           ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
56271         }
56272       }
56273       }
56274       if (!file) cimg::fclose(nfile);
56275       return *this;
56276     }
56277 
56278     //! Save image as a JPEG file.
56279     /**
56280       \param filename Filename, as a C-string.
56281       \param quality Image quality (in %)
56282     **/
56283     const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
56284       return _save_jpeg(0,filename,quality);
56285     }
56286 
56287     //! Save image as a JPEG file \overloading.
56288     const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
56289       return _save_jpeg(file,0,quality);
56290     }
56291 
56292     const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
56293       if (!file && !filename)
56294         throw CImgArgumentException(_cimg_instance
56295                                     "save_jpeg(): Specified filename is (null).",
56296                                     cimg_instance);
56297       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56298       if (_depth>1)
56299         cimg::warn(_cimg_instance
56300                    "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56301                    cimg_instance,
56302                    filename?filename:"(FILE*)");
56303 
56304 #ifndef cimg_use_jpeg
56305       if (!file) return save_other(filename,quality);
56306       else throw CImgIOException(_cimg_instance
56307                                  "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
56308                                  cimg_instance);
56309 #else
56310       unsigned int dimbuf = 0;
56311       J_COLOR_SPACE colortype = JCS_RGB;
56312 
56313       switch (_spectrum) {
56314       case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
56315       case 2 : dimbuf = 3; colortype = JCS_RGB; break;
56316       case 3 : dimbuf = 3; colortype = JCS_RGB; break;
56317       default : dimbuf = 4; colortype = JCS_CMYK; break;
56318       }
56319 
56320       // Call libjpeg functions
56321       struct jpeg_compress_struct cinfo;
56322       struct jpeg_error_mgr jerr;
56323       cinfo.err = jpeg_std_error(&jerr);
56324       jpeg_create_compress(&cinfo);
56325       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
56326       jpeg_stdio_dest(&cinfo,nfile);
56327       cinfo.image_width = _width;
56328       cinfo.image_height = _height;
56329       cinfo.input_components = dimbuf;
56330       cinfo.in_color_space = colortype;
56331       jpeg_set_defaults(&cinfo);
56332       jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
56333       jpeg_start_compress(&cinfo,TRUE);
56334 
56335       JSAMPROW row_pointer[1];
56336       CImg<ucharT> buffer(_width*dimbuf);
56337 
56338       while (cinfo.next_scanline<cinfo.image_height) {
56339         unsigned char *ptrd = buffer._data;
56340 
56341         // Fill pixel buffer
56342         switch (_spectrum) {
56343         case 1 : { // Greyscale images
56344           const T *ptr_g = data(0, cinfo.next_scanline);
56345           for (unsigned int b = 0; b<cinfo.image_width; b++)
56346             *(ptrd++) = (unsigned char)*(ptr_g++);
56347         } break;
56348         case 2 : { // RG images
56349           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
56350             *ptr_g = data(0,cinfo.next_scanline,0,1);
56351           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
56352             *(ptrd++) = (unsigned char)*(ptr_r++);
56353             *(ptrd++) = (unsigned char)*(ptr_g++);
56354             *(ptrd++) = 0;
56355           }
56356         } break;
56357         case 3 : { // RGB images
56358           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
56359             *ptr_g = data(0,cinfo.next_scanline,0,1),
56360             *ptr_b = data(0,cinfo.next_scanline,0,2);
56361           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
56362             *(ptrd++) = (unsigned char)*(ptr_r++);
56363             *(ptrd++) = (unsigned char)*(ptr_g++);
56364             *(ptrd++) = (unsigned char)*(ptr_b++);
56365           }
56366         } break;
56367         default : { // CMYK images
56368           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
56369             *ptr_g = data(0,cinfo.next_scanline,0,1),
56370             *ptr_b = data(0,cinfo.next_scanline,0,2),
56371             *ptr_a = data(0,cinfo.next_scanline,0,3);
56372           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
56373             *(ptrd++) = (unsigned char)*(ptr_r++);
56374             *(ptrd++) = (unsigned char)*(ptr_g++);
56375             *(ptrd++) = (unsigned char)*(ptr_b++);
56376             *(ptrd++) = (unsigned char)*(ptr_a++);
56377           }
56378         }
56379         }
56380         *row_pointer = buffer._data;
56381         jpeg_write_scanlines(&cinfo,row_pointer,1);
56382       }
56383       jpeg_finish_compress(&cinfo);
56384       if (!file) cimg::fclose(nfile);
56385       jpeg_destroy_compress(&cinfo);
56386       return *this;
56387 #endif
56388     }
56389 
56390     //! Save image, using built-in ImageMagick++ library.
56391     /**
56392       \param filename Filename, as a C-string.
56393       \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
56394     **/
56395     const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
56396       if (!filename)
56397         throw CImgArgumentException(_cimg_instance
56398                                     "save_magick(): Specified filename is (null).",
56399                                     cimg_instance);
56400       if (is_empty()) { cimg::fempty(0,filename); return *this; }
56401 
56402 #ifdef cimg_use_magick
56403       double stmin, stmax = (double)max_min(stmin);
56404       if (_depth>1)
56405         cimg::warn(_cimg_instance
56406                    "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56407                    cimg_instance,
56408                    filename);
56409 
56410       if (_spectrum>3)
56411         cimg::warn(_cimg_instance
56412                    "save_magick(): Instance is multispectral, only the three first channels will be "
56413                    "saved in file '%s'.",
56414                    cimg_instance,
56415                    filename);
56416 
56417       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
56418         cimg::warn(_cimg_instance
56419                    "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
56420                    cimg_instance,
56421                    filename,stmin,stmax);
56422 
56423       Magick::Image image(Magick::Geometry(_width,_height),"black");
56424       image.type(Magick::TrueColorType);
56425       image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
56426       const T
56427         *ptr_r = data(0,0,0,0),
56428         *ptr_g = _spectrum>1?data(0,0,0,1):0,
56429         *ptr_b = _spectrum>2?data(0,0,0,2):0;
56430       Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
56431       switch (_spectrum) {
56432       case 1 : // Scalar images
56433         for (ulongT off = (ulongT)_width*_height; off; --off) {
56434           pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
56435           ++pixels;
56436         }
56437         break;
56438       case 2 : // RG images
56439         for (ulongT off = (ulongT)_width*_height; off; --off) {
56440           pixels->red = (Magick::Quantum)*(ptr_r++);
56441           pixels->green = (Magick::Quantum)*(ptr_g++);
56442           pixels->blue = 0; ++pixels;
56443         }
56444         break;
56445       default : // RGB images
56446         for (ulongT off = (ulongT)_width*_height; off; --off) {
56447           pixels->red = (Magick::Quantum)*(ptr_r++);
56448           pixels->green = (Magick::Quantum)*(ptr_g++);
56449           pixels->blue = (Magick::Quantum)*(ptr_b++);
56450           ++pixels;
56451         }
56452       }
56453       image.syncPixels();
56454       image.write(filename);
56455       return *this;
56456 #else
56457       cimg::unused(bytes_per_pixel);
56458       throw CImgIOException(_cimg_instance
56459                             "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
56460                             cimg_instance,
56461                             filename);
56462 #endif
56463     }
56464 
56465     //! Save image as a PNG file.
56466     /**
56467        \param filename Filename, as a C-string.
56468        \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
56469     **/
56470     const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
56471       return _save_png(0,filename,bytes_per_pixel);
56472     }
56473 
56474     //! Save image as a PNG file \overloading.
56475     const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
56476       return _save_png(file,0,bytes_per_pixel);
56477     }
56478 
56479     const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
56480                              const unsigned int bytes_per_pixel=0) const {
56481       if (!file && !filename)
56482         throw CImgArgumentException(_cimg_instance
56483                                     "save_png(): Specified filename is (null).",
56484                                     cimg_instance);
56485       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56486 
56487 #ifndef cimg_use_png
56488       cimg::unused(bytes_per_pixel);
56489       if (!file) return save_other(filename);
56490       else throw CImgIOException(_cimg_instance
56491                                  "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
56492                                  cimg_instance);
56493 #else
56494 
56495 #if defined __GNUC__
56496       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
56497       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
56498       volatile double stmin, stmax = (double)max_min(stmin);
56499 #else
56500       const char *nfilename = filename;
56501       std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb");
56502       double stmin, stmax = (double)max_min(stmin);
56503 #endif
56504 
56505       if (_depth>1)
56506         cimg::warn(_cimg_instance
56507                    "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56508                    cimg_instance,
56509                    filename);
56510 
56511       if (_spectrum>4)
56512         cimg::warn(_cimg_instance
56513                    "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
56514                    cimg_instance,
56515                    filename);
56516 
56517       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
56518         cimg::warn(_cimg_instance
56519                    "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
56520                    cimg_instance,
56521                    filename,stmin,stmax);
56522 
56523       // Setup PNG structures for write
56524       png_voidp user_error_ptr = 0;
56525       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
56526       png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
56527                                                     user_warning_fn);
56528       if (!png_ptr){
56529         if (!file) cimg::fclose(nfile);
56530         throw CImgIOException(_cimg_instance
56531                               "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
56532                               cimg_instance,
56533                               nfilename?nfilename:"(FILE*)");
56534       }
56535       png_infop info_ptr = png_create_info_struct(png_ptr);
56536       if (!info_ptr) {
56537         png_destroy_write_struct(&png_ptr,(png_infopp)0);
56538         if (!file) cimg::fclose(nfile);
56539         throw CImgIOException(_cimg_instance
56540                               "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
56541                               cimg_instance,
56542                               nfilename?nfilename:"(FILE*)");
56543       }
56544       if (setjmp(png_jmpbuf(png_ptr))) {
56545         png_destroy_write_struct(&png_ptr, &info_ptr);
56546         if (!file) cimg::fclose(nfile);
56547         throw CImgIOException(_cimg_instance
56548                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
56549                               cimg_instance,
56550                               nfilename?nfilename:"(FILE*)");
56551       }
56552       png_init_io(png_ptr, nfile);
56553 
56554       const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
56555 
56556       int color_type;
56557       switch (spectrum()) {
56558       case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
56559       case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
56560       case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
56561       default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
56562       }
56563       const int interlace_type = PNG_INTERLACE_NONE;
56564       const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
56565       const int filter_method = PNG_FILTER_TYPE_DEFAULT;
56566       png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
56567       png_write_info(png_ptr,info_ptr);
56568       const int byte_depth = bit_depth>>3;
56569       const int numChan = spectrum()>4?4:spectrum();
56570       const int pixel_bit_depth_flag = numChan * (bit_depth - 1);
56571 
56572       // Allocate Memory for Image Save and Fill pixel data
56573       png_bytep *const imgData = new png_byte*[_height];
56574       for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
56575       const T *pC0 = data(0,0,0,0);
56576       switch (pixel_bit_depth_flag) {
56577       case 7 :  { // Gray 8-bit
56578         cimg_forY(*this,y) {
56579           unsigned char *ptrd = imgData[y];
56580           cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
56581         }
56582       } break;
56583       case 14 : { // Gray w/ Alpha 8-bit
56584         const T *pC1 = data(0,0,0,1);
56585         cimg_forY(*this,y) {
56586           unsigned char *ptrd = imgData[y];
56587           cimg_forX(*this,x) {
56588             *(ptrd++) = (unsigned char)*(pC0++);
56589             *(ptrd++) = (unsigned char)*(pC1++);
56590           }
56591         }
56592       } break;
56593       case 21 :  { // RGB 8-bit
56594         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
56595         cimg_forY(*this,y) {
56596           unsigned char *ptrd = imgData[y];
56597           cimg_forX(*this,x) {
56598             *(ptrd++) = (unsigned char)*(pC0++);
56599             *(ptrd++) = (unsigned char)*(pC1++);
56600             *(ptrd++) = (unsigned char)*(pC2++);
56601           }
56602         }
56603       } break;
56604       case 28 : { // RGB x/ Alpha 8-bit
56605         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
56606         cimg_forY(*this,y){
56607           unsigned char *ptrd = imgData[y];
56608           cimg_forX(*this,x){
56609             *(ptrd++) = (unsigned char)*(pC0++);
56610             *(ptrd++) = (unsigned char)*(pC1++);
56611             *(ptrd++) = (unsigned char)*(pC2++);
56612             *(ptrd++) = (unsigned char)*(pC3++);
56613           }
56614         }
56615       } break;
56616       case 15 : { // Gray 16-bit
56617         cimg_forY(*this,y){
56618           unsigned short *ptrd = (unsigned short*)(imgData[y]);
56619           cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
56620           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
56621         }
56622       } break;
56623       case 30 : { // Gray w/ Alpha 16-bit
56624         const T *pC1 = data(0,0,0,1);
56625         cimg_forY(*this,y){
56626           unsigned short *ptrd = (unsigned short*)(imgData[y]);
56627           cimg_forX(*this,x) {
56628             *(ptrd++) = (unsigned short)*(pC0++);
56629             *(ptrd++) = (unsigned short)*(pC1++);
56630           }
56631           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
56632         }
56633       } break;
56634       case 45 : { // RGB 16-bit
56635         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
56636         cimg_forY(*this,y) {
56637           unsigned short *ptrd = (unsigned short*)(imgData[y]);
56638           cimg_forX(*this,x) {
56639             *(ptrd++) = (unsigned short)*(pC0++);
56640             *(ptrd++) = (unsigned short)*(pC1++);
56641             *(ptrd++) = (unsigned short)*(pC2++);
56642           }
56643           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
56644         }
56645       } break;
56646       case 60 : { // RGB w/ Alpha 16-bit
56647         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
56648         cimg_forY(*this,y) {
56649           unsigned short *ptrd = (unsigned short*)(imgData[y]);
56650           cimg_forX(*this,x) {
56651             *(ptrd++) = (unsigned short)*(pC0++);
56652             *(ptrd++) = (unsigned short)*(pC1++);
56653             *(ptrd++) = (unsigned short)*(pC2++);
56654             *(ptrd++) = (unsigned short)*(pC3++);
56655           }
56656           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
56657         }
56658       } break;
56659       default :
56660         if (!file) cimg::fclose(nfile);
56661         throw CImgIOException(_cimg_instance
56662                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
56663                               cimg_instance,
56664                               nfilename?nfilename:"(FILE*)");
56665       }
56666       png_write_image(png_ptr,imgData);
56667       png_write_end(png_ptr,info_ptr);
56668       png_destroy_write_struct(&png_ptr, &info_ptr);
56669 
56670       // Deallocate Image Write Memory
56671       cimg_forY(*this,n) delete[] imgData[n];
56672       delete[] imgData;
56673 
56674       if (!file) cimg::fclose(nfile);
56675       return *this;
56676 #endif
56677     }
56678 
56679     //! Save image as a PNM file.
56680     /**
56681       \param filename Filename, as a C-string.
56682       \param bytes_per_pixel Force the number of bytes per pixels for the saving.
56683     **/
56684     const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
56685       return _save_pnm(0,filename,bytes_per_pixel);
56686     }
56687 
56688     //! Save image as a PNM file \overloading.
56689     const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
56690       return _save_pnm(file,0,bytes_per_pixel);
56691     }
56692 
56693     const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
56694                              const unsigned int bytes_per_pixel=0) const {
56695       if (!file && !filename)
56696         throw CImgArgumentException(_cimg_instance
56697                                     "save_pnm(): Specified filename is (null).",
56698                                     cimg_instance);
56699       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56700 
56701       double stmin, stmax = (double)max_min(stmin);
56702       if (_depth>1)
56703         cimg::warn(_cimg_instance
56704                    "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56705                    cimg_instance,
56706                    filename?filename:"(FILE*)");
56707       if (_spectrum>3)
56708         cimg::warn(_cimg_instance
56709                    "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
56710                    cimg_instance,
56711                    filename?filename:"(FILE*)");
56712       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
56713         cimg::warn(_cimg_instance
56714                    "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
56715                    cimg_instance,
56716                    stmin,stmax,filename?filename:"(FILE*)");
56717 
56718       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
56719       const T
56720         *ptr_r = data(0,0,0,0),
56721         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
56722         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
56723       const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL)));
56724 
56725       std::fprintf(nfile,"P%c\n%u %u\n%u\n",
56726                    (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
56727 
56728       switch (_spectrum) {
56729       case 1 : { // Scalar image
56730         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
56731           CImg<ucharT> buf((unsigned int)buf_size);
56732           for (longT to_write = (longT)width()*height(); to_write>0; ) {
56733             const ulongT N = std::min((ulongT)to_write,buf_size);
56734             unsigned char *ptrd = buf._data;
56735             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
56736             cimg::fwrite(buf._data,N,nfile);
56737             to_write-=N;
56738           }
56739         } else { // Binary PGM 16 bits
56740           CImg<ushortT> buf((unsigned int)buf_size);
56741           for (longT to_write = (longT)width()*height(); to_write>0; ) {
56742             const ulongT N = std::min((ulongT)to_write,buf_size);
56743             unsigned short *ptrd = buf._data;
56744             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
56745             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
56746             cimg::fwrite(buf._data,N,nfile);
56747             to_write-=N;
56748           }
56749         }
56750       } break;
56751       case 2 : { // RG image
56752         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
56753           CImg<ucharT> buf((unsigned int)buf_size);
56754           for (longT to_write = (longT)width()*height(); to_write>0; ) {
56755             const ulongT N = std::min((ulongT)to_write,buf_size/3);
56756             unsigned char *ptrd = buf._data;
56757             for (ulongT i = N; i>0; --i) {
56758               *(ptrd++) = (unsigned char)*(ptr_r++);
56759               *(ptrd++) = (unsigned char)*(ptr_g++);
56760               *(ptrd++) = 0;
56761             }
56762             cimg::fwrite(buf._data,3*N,nfile);
56763             to_write-=N;
56764           }
56765         } else {             // Binary PPM 16 bits
56766           CImg<ushortT> buf((unsigned int)buf_size);
56767           for (longT to_write = (longT)width()*height(); to_write>0; ) {
56768             const ulongT N = std::min((ulongT)to_write,buf_size/3);
56769             unsigned short *ptrd = buf._data;
56770             for (ulongT i = N; i>0; --i) {
56771               *(ptrd++) = (unsigned short)*(ptr_r++);
56772               *(ptrd++) = (unsigned short)*(ptr_g++);
56773               *(ptrd++) = 0;
56774             }
56775             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
56776             cimg::fwrite(buf._data,3*N,nfile);
56777             to_write-=N;
56778           }
56779         }
56780       } break;
56781       default : { // RGB image
56782         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
56783           CImg<ucharT> buf((unsigned int)buf_size);
56784           for (longT to_write = (longT)width()*height(); to_write>0; ) {
56785             const ulongT N = std::min((ulongT)to_write,buf_size/3);
56786             unsigned char *ptrd = buf._data;
56787             for (ulongT i = N; i>0; --i) {
56788               *(ptrd++) = (unsigned char)*(ptr_r++);
56789               *(ptrd++) = (unsigned char)*(ptr_g++);
56790               *(ptrd++) = (unsigned char)*(ptr_b++);
56791             }
56792             cimg::fwrite(buf._data,3*N,nfile);
56793             to_write-=N;
56794           }
56795         } else { // Binary PPM 16 bits
56796           CImg<ushortT> buf((unsigned int)buf_size);
56797           for (longT to_write = (longT)width()*height(); to_write>0; ) {
56798             const ulongT N = std::min((ulongT)to_write,buf_size/3);
56799             unsigned short *ptrd = buf._data;
56800             for (ulongT i = N; i>0; --i) {
56801               *(ptrd++) = (unsigned short)*(ptr_r++);
56802               *(ptrd++) = (unsigned short)*(ptr_g++);
56803               *(ptrd++) = (unsigned short)*(ptr_b++);
56804             }
56805             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
56806             cimg::fwrite(buf._data,3*N,nfile);
56807             to_write-=N;
56808           }
56809         }
56810       }
56811       }
56812       if (!file) cimg::fclose(nfile);
56813       return *this;
56814     }
56815 
56816     //! Save image as a PNK file.
56817     /**
56818       \param filename Filename, as a C-string.
56819     **/
56820     const CImg<T>& save_pnk(const char *const filename) const {
56821       return _save_pnk(0,filename);
56822     }
56823 
56824     //! Save image as a PNK file \overloading.
56825     const CImg<T>& save_pnk(std::FILE *const file) const {
56826       return _save_pnk(file,0);
56827     }
56828 
56829     const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
56830       if (!file && !filename)
56831         throw CImgArgumentException(_cimg_instance
56832                                     "save_pnk(): Specified filename is (null).",
56833                                     cimg_instance);
56834       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56835       if (_spectrum>1)
56836         cimg::warn(_cimg_instance
56837                    "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
56838                    cimg_instance,
56839                    filename?filename:"(FILE*)");
56840 
56841       const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth);
56842       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
56843       const T *ptr = data(0,0,0,0);
56844 
56845       if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file
56846         _save_pnm(file,filename,0);
56847       else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D
56848         std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
56849         CImg<ucharT> buf((unsigned int)buf_size);
56850         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
56851           const ulongT N = std::min((ulongT)to_write,buf_size);
56852           unsigned char *ptrd = buf._data;
56853           for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
56854           cimg::fwrite(buf._data,N,nfile);
56855           to_write-=N;
56856         }
56857       } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3D
56858         if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
56859         else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
56860         CImg<intT> buf((unsigned int)buf_size);
56861         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
56862           const ulongT N = std::min((ulongT)to_write,buf_size);
56863           int *ptrd = buf._data;
56864           for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
56865           cimg::fwrite(buf._data,N,nfile);
56866           to_write-=N;
56867         }
56868       } else { // Save as P9: Binary float-valued 3D
56869         if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
56870         else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
56871         CImg<floatT> buf((unsigned int)buf_size);
56872         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
56873           const ulongT N = std::min((ulongT)to_write,buf_size);
56874           float *ptrd = buf._data;
56875           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
56876           cimg::fwrite(buf._data,N,nfile);
56877           to_write-=N;
56878         }
56879       }
56880 
56881       if (!file) cimg::fclose(nfile);
56882       return *this;
56883     }
56884 
56885     //! Save image as a PFM file.
56886     /**
56887       \param filename Filename, as a C-string.
56888     **/
56889     const CImg<T>& save_pfm(const char *const filename) const {
56890       get_mirror('y')._save_pfm(0,filename);
56891       return *this;
56892     }
56893 
56894     //! Save image as a PFM file \overloading.
56895     const CImg<T>& save_pfm(std::FILE *const file) const {
56896       get_mirror('y')._save_pfm(file,0);
56897       return *this;
56898     }
56899 
56900     const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
56901       if (!file && !filename)
56902         throw CImgArgumentException(_cimg_instance
56903                                     "save_pfm(): Specified filename is (null).",
56904                                     cimg_instance);
56905       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56906       if (_depth>1)
56907         cimg::warn(_cimg_instance
56908                    "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
56909                    cimg_instance,
56910                    filename?filename:"(FILE*)");
56911       if (_spectrum>3)
56912         cimg::warn(_cimg_instance
56913                    "save_pfm(): image instance is multispectral, only the three first channels will be saved "
56914                    "in file '%s'.",
56915                    cimg_instance,
56916                    filename?filename:"(FILE*)");
56917 
56918       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
56919       const T
56920         *ptr_r = data(0,0,0,0),
56921         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
56922         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
56923       const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
56924 
56925       std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
56926                    (_spectrum==1?'f':'F'),_width,_height);
56927 
56928       switch (_spectrum) {
56929       case 1 : { // Scalar image
56930         CImg<floatT> buf(buf_size);
56931         for (longT to_write = (longT)width()*height(); to_write>0; ) {
56932           const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size);
56933           float *ptrd = buf._data;
56934           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
56935           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
56936           cimg::fwrite(buf._data,N,nfile);
56937           to_write-=N;
56938         }
56939       } break;
56940       case 2 : { // RG image
56941         CImg<floatT> buf(buf_size);
56942         for (longT to_write = (longT)width()*height(); to_write>0; ) {
56943           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
56944           float *ptrd = buf._data;
56945           for (ulongT i = N; i>0; --i) {
56946             *(ptrd++) = (float)*(ptr_r++);
56947             *(ptrd++) = (float)*(ptr_g++);
56948             *(ptrd++) = 0;
56949           }
56950           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
56951           cimg::fwrite(buf._data,3*N,nfile);
56952           to_write-=N;
56953         }
56954       } break;
56955       default : { // RGB image
56956         CImg<floatT> buf(buf_size);
56957         for (longT to_write = (longT)width()*height(); to_write>0; ) {
56958           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
56959           float *ptrd = buf._data;
56960           for (ulongT i = N; i>0; --i) {
56961             *(ptrd++) = (float)*(ptr_r++);
56962             *(ptrd++) = (float)*(ptr_g++);
56963             *(ptrd++) = (float)*(ptr_b++);
56964           }
56965           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
56966           cimg::fwrite(buf._data,3*N,nfile);
56967           to_write-=N;
56968         }
56969       }
56970       }
56971       if (!file) cimg::fclose(nfile);
56972       return *this;
56973     }
56974 
56975     //! Save image as a RGB file.
56976     /**
56977       \param filename Filename, as a C-string.
56978     **/
56979     const CImg<T>& save_rgb(const char *const filename) const {
56980       return _save_rgb(0,filename);
56981     }
56982 
56983     //! Save image as a RGB file \overloading.
56984     const CImg<T>& save_rgb(std::FILE *const file) const {
56985       return _save_rgb(file,0);
56986     }
56987 
56988     const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
56989       if (!file && !filename)
56990         throw CImgArgumentException(_cimg_instance
56991                                     "save_rgb(): Specified filename is (null).",
56992                                     cimg_instance);
56993       if (is_empty()) { cimg::fempty(file,filename); return *this; }
56994       if (_spectrum!=3)
56995         cimg::warn(_cimg_instance
56996                    "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
56997                    cimg_instance,
56998                    filename?filename:"(FILE*)");
56999 
57000       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57001       const ulongT wh = (ulongT)_width*_height;
57002       unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
57003       const T
57004         *ptr1 = data(0,0,0,0),
57005         *ptr2 = _spectrum>1?data(0,0,0,1):0,
57006         *ptr3 = _spectrum>2?data(0,0,0,2):0;
57007       switch (_spectrum) {
57008       case 1 : { // Scalar image
57009         for (ulongT k = 0; k<wh; ++k) {
57010           const unsigned char val = (unsigned char)*(ptr1++);
57011           *(nbuffer++) = val;
57012           *(nbuffer++) = val;
57013           *(nbuffer++) = val;
57014         }
57015       } break;
57016       case 2 : { // RG image
57017         for (ulongT k = 0; k<wh; ++k) {
57018           *(nbuffer++) = (unsigned char)(*(ptr1++));
57019           *(nbuffer++) = (unsigned char)(*(ptr2++));
57020           *(nbuffer++) = 0;
57021         }
57022       } break;
57023       default : { // RGB image
57024         for (ulongT k = 0; k<wh; ++k) {
57025           *(nbuffer++) = (unsigned char)(*(ptr1++));
57026           *(nbuffer++) = (unsigned char)(*(ptr2++));
57027           *(nbuffer++) = (unsigned char)(*(ptr3++));
57028         }
57029       }
57030       }
57031       cimg::fwrite(buffer,3*wh,nfile);
57032       if (!file) cimg::fclose(nfile);
57033       delete[] buffer;
57034       return *this;
57035     }
57036 
57037     //! Save image as a RGBA file.
57038     /**
57039        \param filename Filename, as a C-string.
57040     **/
57041     const CImg<T>& save_rgba(const char *const filename) const {
57042       return _save_rgba(0,filename);
57043     }
57044 
57045     //! Save image as a RGBA file \overloading.
57046     const CImg<T>& save_rgba(std::FILE *const file) const {
57047       return _save_rgba(file,0);
57048     }
57049 
57050     const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
57051       if (!file && !filename)
57052         throw CImgArgumentException(_cimg_instance
57053                                     "save_rgba(): Specified filename is (null).",
57054                                     cimg_instance);
57055       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57056       if (_spectrum!=4)
57057         cimg::warn(_cimg_instance
57058                    "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
57059                    cimg_instance,
57060                    filename?filename:"(FILE*)");
57061 
57062       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57063       const ulongT wh = (ulongT)_width*_height;
57064       unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
57065       const T
57066         *ptr1 = data(0,0,0,0),
57067         *ptr2 = _spectrum>1?data(0,0,0,1):0,
57068         *ptr3 = _spectrum>2?data(0,0,0,2):0,
57069         *ptr4 = _spectrum>3?data(0,0,0,3):0;
57070       switch (_spectrum) {
57071       case 1 : { // Scalar images
57072         for (ulongT k = 0; k<wh; ++k) {
57073           const unsigned char val = (unsigned char)*(ptr1++);
57074           *(nbuffer++) = val;
57075           *(nbuffer++) = val;
57076           *(nbuffer++) = val;
57077           *(nbuffer++) = 255;
57078         }
57079       } break;
57080       case 2 : { // RG images
57081         for (ulongT k = 0; k<wh; ++k) {
57082           *(nbuffer++) = (unsigned char)(*(ptr1++));
57083           *(nbuffer++) = (unsigned char)(*(ptr2++));
57084           *(nbuffer++) = 0;
57085           *(nbuffer++) = 255;
57086         }
57087       } break;
57088       case 3 : { // RGB images
57089         for (ulongT k = 0; k<wh; ++k) {
57090           *(nbuffer++) = (unsigned char)(*(ptr1++));
57091           *(nbuffer++) = (unsigned char)(*(ptr2++));
57092           *(nbuffer++) = (unsigned char)(*(ptr3++));
57093           *(nbuffer++) = 255;
57094         }
57095       } break;
57096       default : { // RGBA images
57097         for (ulongT k = 0; k<wh; ++k) {
57098           *(nbuffer++) = (unsigned char)(*(ptr1++));
57099           *(nbuffer++) = (unsigned char)(*(ptr2++));
57100           *(nbuffer++) = (unsigned char)(*(ptr3++));
57101           *(nbuffer++) = (unsigned char)(*(ptr4++));
57102         }
57103       }
57104       }
57105       cimg::fwrite(buffer,4*wh,nfile);
57106       if (!file) cimg::fclose(nfile);
57107       delete[] buffer;
57108       return *this;
57109     }
57110 
57111     //! Save image as a TIFF file.
57112     /**
57113        \param filename Filename, as a C-string.
57114        \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>.
57115        \param voxel_size Voxel size, to be stored in the filename.
57116        \param description Description, to be stored in the filename.
57117        \param use_bigtiff Allow to save big tiff files (>4Gb).
57118        \note
57119        - libtiff support is enabled by defining the precompilation
57120         directive \c cimg_use_tif.
57121        - When libtiff is enabled, 2D and 3D (multipage) several
57122         channel per pixel are supported for
57123         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
57124        - If \c cimg_use_tif is not defined at compile time the
57125         function uses CImg<T>&save_other(const char*).
57126      **/
57127     const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
57128                              const float *const voxel_size=0, const char *const description=0,
57129                              const bool use_bigtiff=true) const {
57130       if (!filename)
57131         throw CImgArgumentException(_cimg_instance
57132                                     "save_tiff(): Specified filename is (null).",
57133                                     cimg_instance);
57134       if (is_empty()) { cimg::fempty(0,filename); return *this; }
57135 
57136 #ifdef cimg_use_tiff
57137       const bool
57138         _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images
57139       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
57140       if (tif) {
57141         cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description);
57142         TIFFClose(tif);
57143       } else throw CImgIOException(_cimg_instance
57144                                    "save_tiff(): Failed to open file '%s' for writing.",
57145                                    cimg_instance,
57146                                    filename);
57147       return *this;
57148 #else
57149       cimg::unused(compression_type,voxel_size,description,use_bigtiff);
57150       return save_other(filename);
57151 #endif
57152     }
57153 
57154 #ifdef cimg_use_tiff
57155 
57156 #define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \
57157       const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); }
57158 
57159     // [internal] Save a plane into a tiff file
57160     template<typename t>
57161     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t,
57162                               const unsigned int compression_type, const float *const voxel_size,
57163                               const char *const description) const {
57164       if (is_empty() || !tif || pixel_t) return *this;
57165       const char *const filename = TIFFFileName(tif);
57166       uint32 rowsperstrip = (uint32)-1;
57167       uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
57168       if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
57169       else photometric = PHOTOMETRIC_MINISBLACK;
57170       TIFFSetDirectory(tif,directory);
57171       TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
57172       TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
57173       if (voxel_size) {
57174         const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2];
57175         TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE);
57176         TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx);
57177         TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy);
57178         CImg<charT> s_description(256);
57179         cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz);
57180         TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data());
57181       }
57182       if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description);
57183       TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
57184       TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
57185       if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
57186       else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
57187       else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
57188       double valm, valM = max_min(valm);
57189       TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm);
57190       TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM);
57191       TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
57192       TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
57193       TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
57194       TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG:
57195                    compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE);
57196       rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
57197       TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
57198       TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
57199       TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
57200 
57201       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
57202       if (buf) {
57203         for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
57204           uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip);
57205           tstrip_t strip = TIFFComputeStrip(tif,row,0);
57206           tsize_t i = 0;
57207           for (unsigned int rr = 0; rr<nrow; ++rr)
57208             for (unsigned int cc = 0; cc<_width; ++cc)
57209               for (unsigned int vv = 0; vv<spp; ++vv)
57210                 buf[i++] = (t)(*this)(cc,row + rr,z,vv);
57211           if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
57212             throw CImgIOException(_cimg_instance
57213                                   "save_tiff(): Invalid strip writing when saving file '%s'.",
57214                                   cimg_instance,
57215                                   filename?filename:"(FILE*)");
57216         }
57217         _TIFFfree(buf);
57218       }
57219       TIFFWriteDirectory(tif);
57220       return *this;
57221     }
57222 
57223     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z,
57224                               const unsigned int compression_type, const float *const voxel_size,
57225                               const char *const description) const {
57226       _cimg_save_tiff("bool",unsigned char,compression_type);
57227       _cimg_save_tiff("unsigned char",unsigned char,compression_type);
57228       _cimg_save_tiff("char",char,compression_type);
57229       _cimg_save_tiff("unsigned short",unsigned short,compression_type);
57230       _cimg_save_tiff("short",short,compression_type);
57231       _cimg_save_tiff("unsigned int",unsigned int,compression_type);
57232       _cimg_save_tiff("int",int,compression_type);
57233       _cimg_save_tiff("unsigned int64",unsigned int,compression_type);
57234       _cimg_save_tiff("int64",int,compression_type);
57235       _cimg_save_tiff("float",float,compression_type);
57236       _cimg_save_tiff("double",float,compression_type);
57237       const char *const filename = TIFFFileName(tif);
57238       throw CImgInstanceException(_cimg_instance
57239                                   "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
57240                                   cimg_instance,
57241                                   pixel_type(),filename?filename:"(FILE*)");
57242       return *this;
57243     }
57244 #endif
57245 
57246     //! Save image as a MINC2 file.
57247     /**
57248        \param filename Filename, as a C-string.
57249        \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
57250     **/
57251     const CImg<T>& save_minc2(const char *const filename,
57252                               const char *const imitate_file=0) const {
57253       if (!filename)
57254         throw CImgArgumentException(_cimg_instance
57255                                    "save_minc2(): Specified filename is (null).",
57256                                    cimg_instance);
57257       if (is_empty()) { cimg::fempty(0,filename); return *this; }
57258 
57259 #ifndef cimg_use_minc2
57260      cimg::unused(imitate_file);
57261      return save_other(filename);
57262 #else
57263      minc::minc_1_writer wtr;
57264      if (imitate_file)
57265        wtr.open(filename, imitate_file);
57266      else {
57267        minc::minc_info di;
57268        if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X));
57269        if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y));
57270        if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z));
57271        if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME));
57272        wtr.open(filename,di,1,NC_FLOAT,0);
57273      }
57274      if (pixel_type()==cimg::type<unsigned char>::string())
57275        wtr.setup_write_byte();
57276      else if (pixel_type()==cimg::type<int>::string())
57277        wtr.setup_write_int();
57278      else if (pixel_type()==cimg::type<double>::string())
57279        wtr.setup_write_double();
57280      else
57281        wtr.setup_write_float();
57282      minc::save_standard_volume(wtr, this->_data);
57283      return *this;
57284 #endif
57285     }
57286 
57287     //! Save image as an ANALYZE7.5 or NIFTI file.
57288     /**
57289       \param filename Filename, as a C-string.
57290       \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
57291     **/
57292     const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
57293       if (!filename)
57294         throw CImgArgumentException(_cimg_instance
57295                                     "save_analyze(): Specified filename is (null).",
57296                                     cimg_instance);
57297       if (is_empty()) { cimg::fempty(0,filename); return *this; }
57298 
57299       std::FILE *file;
57300       CImg<charT> hname(1024), iname(1024);
57301       const char *const ext = cimg::split_filename(filename);
57302       short datatype = -1;
57303       if (!*ext) {
57304         cimg_snprintf(hname,hname._width,"%s.hdr",filename);
57305         cimg_snprintf(iname,iname._width,"%s.img",filename);
57306       }
57307       if (!cimg::strncasecmp(ext,"hdr",3)) {
57308         std::strcpy(hname,filename);
57309         std::strncpy(iname,filename,iname._width - 1);
57310         cimg_sprintf(iname._data + std::strlen(iname) - 3,"img");
57311       }
57312       if (!cimg::strncasecmp(ext,"img",3)) {
57313         std::strcpy(hname,filename);
57314         std::strncpy(iname,filename,iname._width - 1);
57315         cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr");
57316       }
57317       if (!cimg::strncasecmp(ext,"nii",3)) {
57318         std::strncpy(hname,filename,hname._width - 1); *iname = 0;
57319       }
57320 
57321       CImg<charT> header(*iname?348:352,1,1,1,0);
57322       int *const iheader = (int*)header._data;
57323       *iheader = 348;
57324       std::strcpy(header._data + 4,"CImg");
57325       std::strcpy(header._data + 14," ");
57326       ((short*)&(header[36]))[0] = 4096;
57327       ((char*)&(header[38]))[0] = 114;
57328       ((short*)&(header[40]))[0] = 4;
57329       ((short*)&(header[40]))[1] = (short)_width;
57330       ((short*)&(header[40]))[2] = (short)_height;
57331       ((short*)&(header[40]))[3] = (short)_depth;
57332       ((short*)&(header[40]))[4] = (short)_spectrum;
57333       if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
57334       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
57335       if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
57336       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
57337       if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
57338       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
57339       if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
57340       if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8;
57341       if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8;
57342       if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
57343       if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
57344       if (datatype<0)
57345         throw CImgIOException(_cimg_instance
57346                               "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
57347                               cimg_instance,
57348                               pixel_type(),filename);
57349 
57350       ((short*)&(header[70]))[0] = datatype;
57351       ((short*)&(header[72]))[0] = sizeof(T);
57352       ((float*)&(header[108]))[0] = (float)(*iname?0:header.width());
57353       ((float*)&(header[112]))[0] = 1;
57354       ((float*)&(header[76]))[0] = 0;
57355       if (voxel_size) {
57356         ((float*)&(header[76]))[1] = voxel_size[0];
57357         ((float*)&(header[76]))[2] = voxel_size[1];
57358         ((float*)&(header[76]))[3] = voxel_size[2];
57359       } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1;
57360       file = cimg::fopen(hname,"wb");
57361       cimg::fwrite(header._data,header.width(),file);
57362       if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
57363       cimg::fwrite(_data,size(),file);
57364       cimg::fclose(file);
57365       return *this;
57366     }
57367 
57368     //! Save image as a .cimg file.
57369     /**
57370       \param filename Filename, as a C-string.
57371       \param is_compressed Tells if the file contains compressed image data.
57372     **/
57373     const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
57374       CImgList<T>(*this,true).save_cimg(filename,is_compressed);
57375       return *this;
57376     }
57377 
57378     //! Save image as a .cimg file \overloading.
57379     const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
57380       CImgList<T>(*this,true).save_cimg(file,is_compressed);
57381       return *this;
57382     }
57383 
57384     //! Save image as a sub-image into an existing .cimg file.
57385     /**
57386       \param filename Filename, as a C-string.
57387       \param n0 Index of the image inside the file.
57388       \param x0 X-coordinate of the sub-image location.
57389       \param y0 Y-coordinate of the sub-image location.
57390       \param z0 Z-coordinate of the sub-image location.
57391       \param c0 C-coordinate of the sub-image location.
57392     **/
57393     const CImg<T>& save_cimg(const char *const filename,
57394                              const unsigned int n0,
57395                              const unsigned int x0, const unsigned int y0,
57396                              const unsigned int z0, const unsigned int c0) const {
57397       CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
57398       return *this;
57399     }
57400 
57401     //! Save image as a sub-image into an existing .cimg file \overloading.
57402     const CImg<T>& save_cimg(std::FILE *const file,
57403                              const unsigned int n0,
57404                              const unsigned int x0, const unsigned int y0,
57405                              const unsigned int z0, const unsigned int c0) const {
57406       CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
57407       return *this;
57408     }
57409 
57410     //! Save blank image as a .cimg file.
57411     /**
57412         \param filename Filename, as a C-string.
57413         \param dx Width of the image.
57414         \param dy Height of the image.
57415         \param dz Depth of the image.
57416         \param dc Number of channels of the image.
57417         \note
57418         - All pixel values of the saved image are set to \c 0.
57419         - Use this method to save large images without having to instantiate and allocate them.
57420     **/
57421     static void save_empty_cimg(const char *const filename,
57422                                 const unsigned int dx, const unsigned int dy=1,
57423                                 const unsigned int dz=1, const unsigned int dc=1) {
57424       return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
57425     }
57426 
57427     //! Save blank image as a .cimg file \overloading.
57428     /**
57429        Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
57430        with a file stream argument instead of a filename string.
57431     **/
57432     static void save_empty_cimg(std::FILE *const file,
57433                                 const unsigned int dx, const unsigned int dy=1,
57434                                 const unsigned int dz=1, const unsigned int dc=1) {
57435       return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
57436     }
57437 
57438     //! Save image as an INRIMAGE-4 file.
57439     /**
57440       \param filename Filename, as a C-string.
57441       \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
57442     **/
57443     const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
57444       return _save_inr(0,filename,voxel_size);
57445     }
57446 
57447     //! Save image as an INRIMAGE-4 file \overloading.
57448     const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
57449       return _save_inr(file,0,voxel_size);
57450     }
57451 
57452     const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
57453       if (!file && !filename)
57454         throw CImgArgumentException(_cimg_instance
57455                                     "save_inr(): Specified filename is (null).",
57456                                     cimg_instance);
57457       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57458 
57459       int inrpixsize = -1;
57460       const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
57461       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) {
57462         inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
57463       }
57464       if (!cimg::strcasecmp(pixel_type(),"char")) {
57465         inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
57466       }
57467       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) {
57468         inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
57469       }
57470       if (!cimg::strcasecmp(pixel_type(),"short")) {
57471         inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
57472       }
57473       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) {
57474         inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
57475       }
57476       if (!cimg::strcasecmp(pixel_type(),"int")) {
57477         inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
57478       }
57479       if (!cimg::strcasecmp(pixel_type(),"float")) {
57480         inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
57481       }
57482       if (!cimg::strcasecmp(pixel_type(),"double")) {
57483         inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
57484       }
57485       if (inrpixsize<=0)
57486         throw CImgIOException(_cimg_instance
57487                               "save_inr(): Unsupported pixel type '%s' for file '%s'",
57488                               cimg_instance,
57489                               pixel_type(),filename?filename:"(FILE*)");
57490 
57491       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57492       CImg<charT> header(257);
57493       int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
57494                               _width,_height,_depth,_spectrum);
57495       if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n",
57496                                         voxel_size[0],voxel_size[1],voxel_size[2]);
57497       err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
57498       std::memset(header._data + err,'\n',252 - err);
57499       std::memcpy(header._data + 252,"##}\n",4);
57500       cimg::fwrite(header._data,256,nfile);
57501       cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
57502       if (!file) cimg::fclose(nfile);
57503       return *this;
57504     }
57505 
57506     //! Save image as an OpenEXR file.
57507     /**
57508        \param filename Filename, as a C-string.
57509        \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
57510     **/
57511     const CImg<T>& save_exr(const char *const filename) const {
57512       if (!filename)
57513         throw CImgArgumentException(_cimg_instance
57514                                     "save_exr(): Specified filename is (null).",
57515                                     cimg_instance);
57516       if (is_empty()) { cimg::fempty(0,filename); return *this; }
57517       if (_depth>1)
57518         cimg::warn(_cimg_instance
57519                    "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
57520                    cimg_instance,
57521                    filename);
57522 
57523 #ifndef cimg_use_openexr
57524       return save_other(filename);
57525 #else
57526       Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba;
57527       switch (_spectrum) {
57528       case 1 : { // Grayscale image
57529         for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
57530           rgba.r = (half)(*(ptr_r));
57531           rgba.g = (half)(*(ptr_r));
57532           rgba.b = (half)(*(ptr_r++));
57533           rgba.a = (half)1;
57534           *(ptrd++) = rgba;
57535         }
57536       } break;
57537       case 2 : { // RG image
57538         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
57539                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) {
57540           rgba.r = (half)(*(ptr_r++));
57541           rgba.g = (half)(*(ptr_g++));
57542           rgba.b = (half)0;
57543           rgba.a = (half)1;
57544           *(ptrd++) = rgba;
57545         }
57546       } break;
57547       case 3 : { // RGB image
57548         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
57549                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
57550           rgba.r = (half)(*(ptr_r++));
57551           rgba.g = (half)(*(ptr_g++));
57552           rgba.b = (half)(*(ptr_b++));
57553           rgba.a = (half)1;
57554           *(ptrd++) = rgba;
57555         }
57556       } break;
57557       default : { // RGBA image
57558         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),
57559                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
57560           rgba.r = (half)(*(ptr_r++));
57561           rgba.g = (half)(*(ptr_g++));
57562           rgba.b = (half)(*(ptr_b++));
57563           rgba.a = (half)(*(ptr_a++));
57564           *(ptrd++) = rgba;
57565         }
57566       } break;
57567       }
57568       Imf::RgbaOutputFile outFile(filename,_width,_height,
57569                                   _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
57570                                   Imf::WRITE_RGB:Imf::WRITE_RGBA);
57571       outFile.setFrameBuffer(ptrd0,1,_width);
57572       outFile.writePixels(_height);
57573       delete[] ptrd0;
57574       return *this;
57575 #endif
57576     }
57577 
57578     //! Save image as a Pandore-5 file.
57579     /**
57580        \param filename Filename, as a C-string.
57581        \param colorspace Colorspace data field in output file
57582        (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a>
57583        for more information).
57584     **/
57585     const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
57586       return _save_pandore(0,filename,colorspace);
57587     }
57588 
57589     //! Save image as a Pandore-5 file \overloading.
57590     /**
57591         Same as save_pandore(const char *,unsigned int) const
57592         with a file stream argument instead of a filename string.
57593     **/
57594     const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
57595       return _save_pandore(file,0,colorspace);
57596     }
57597 
57598     unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
57599       unsigned int nbdims = 0;
57600       if (id==2 || id==3 || id==4) {
57601         dims[0] = 1; dims[1] = _width; nbdims = 2;
57602       }
57603       if (id==5 || id==6 || id==7) {
57604         dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
57605       }
57606       if (id==8 || id==9 || id==10) {
57607         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
57608       }
57609       if (id==16 || id==17 || id==18) {
57610         dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
57611       }
57612       if (id==19 || id==20 || id==21) {
57613         dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
57614       }
57615       if (id==22 || id==23 || id==25) {
57616         dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
57617       }
57618       if (id==26 || id==27 || id==29) {
57619         dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
57620       }
57621       if (id==30 || id==31 || id==33) {
57622         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
57623       }
57624       return nbdims;
57625     }
57626 
57627     const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
57628                                  const unsigned int colorspace) const {
57629 
57630 #define __cimg_save_pandore_case(dtype) \
57631        dtype *buffer = new dtype[size()]; \
57632        const T *ptrs = _data; \
57633        cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
57634        buffer-=size(); \
57635        cimg::fwrite(buffer,size(),nfile); \
57636        delete[] buffer
57637 
57638 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
57639       if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
57640           (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
57641         unsigned int *iheader = (unsigned int*)(header + 12); \
57642         nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
57643         cimg::fwrite(header,36,nfile); \
57644         if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \
57645           for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; \
57646           cimg::fwrite(ndims._data,nbdims,nfile); } \
57647         else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \
57648           for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; \
57649           cimg::fwrite(ndims._data,nbdims,nfile); } \
57650         else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \
57651           for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; \
57652           cimg::fwrite(ndims._data,nbdims,nfile); } \
57653         else throw CImgIOException(_cimg_instance \
57654                                    "save_pandore(): Unsupported datatype for file '%s'.",\
57655                                    cimg_instance, \
57656                                    filename?filename:"(FILE*)"); \
57657         if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
57658           __cimg_save_pandore_case(unsigned char); \
57659         } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
57660           if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
57661           else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
57662           else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
57663           else throw CImgIOException(_cimg_instance \
57664                                      "save_pandore(): Unsupported datatype for file '%s'.",\
57665                                      cimg_instance, \
57666                                      filename?filename:"(FILE*)"); \
57667         } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
57668           if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
57669           else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
57670           else throw CImgIOException(_cimg_instance \
57671                                      "save_pandore(): Unsupported datatype for file '%s'.",\
57672                                      cimg_instance, \
57673                                      filename?filename:"(FILE*)"); \
57674         } \
57675         saved = true; \
57676       }
57677 
57678       if (!file && !filename)
57679         throw CImgArgumentException(_cimg_instance
57680                                     "save_pandore(): Specified filename is (null).",
57681                                     cimg_instance);
57682       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57683 
57684       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57685       unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
57686                                    0,0,0,0,'C','I','m','g',0,0,0,0,0,
57687                                    'N','o',' ','d','a','t','e',0,0,0,0 };
57688       unsigned int nbdims, dims[5] = { 0 };
57689       bool saved = false;
57690       _cimg_save_pandore_case(1,1,1,"unsigned char",2);
57691       _cimg_save_pandore_case(1,1,1,"char",3);
57692       _cimg_save_pandore_case(1,1,1,"unsigned short",3);
57693       _cimg_save_pandore_case(1,1,1,"short",3);
57694       _cimg_save_pandore_case(1,1,1,"unsigned int",3);
57695       _cimg_save_pandore_case(1,1,1,"int",3);
57696       _cimg_save_pandore_case(1,1,1,"unsigned int64",3);
57697       _cimg_save_pandore_case(1,1,1,"int64",3);
57698       _cimg_save_pandore_case(1,1,1,"float",4);
57699       _cimg_save_pandore_case(1,1,1,"double",4);
57700 
57701       _cimg_save_pandore_case(0,1,1,"unsigned char",5);
57702       _cimg_save_pandore_case(0,1,1,"char",6);
57703       _cimg_save_pandore_case(0,1,1,"unsigned short",6);
57704       _cimg_save_pandore_case(0,1,1,"short",6);
57705       _cimg_save_pandore_case(0,1,1,"unsigned int",6);
57706       _cimg_save_pandore_case(0,1,1,"int",6);
57707       _cimg_save_pandore_case(0,1,1,"unsigned int64",6);
57708       _cimg_save_pandore_case(0,1,1,"int64",6);
57709       _cimg_save_pandore_case(0,1,1,"float",7);
57710       _cimg_save_pandore_case(0,1,1,"double",7);
57711 
57712       _cimg_save_pandore_case(0,0,1,"unsigned char",8);
57713       _cimg_save_pandore_case(0,0,1,"char",9);
57714       _cimg_save_pandore_case(0,0,1,"unsigned short",9);
57715       _cimg_save_pandore_case(0,0,1,"short",9);
57716       _cimg_save_pandore_case(0,0,1,"unsigned int",9);
57717       _cimg_save_pandore_case(0,0,1,"int",9);
57718       _cimg_save_pandore_case(0,0,1,"unsigned int64",9);
57719       _cimg_save_pandore_case(0,0,1,"int64",9);
57720       _cimg_save_pandore_case(0,0,1,"float",10);
57721       _cimg_save_pandore_case(0,0,1,"double",10);
57722 
57723       _cimg_save_pandore_case(0,1,3,"unsigned char",16);
57724       _cimg_save_pandore_case(0,1,3,"char",17);
57725       _cimg_save_pandore_case(0,1,3,"unsigned short",17);
57726       _cimg_save_pandore_case(0,1,3,"short",17);
57727       _cimg_save_pandore_case(0,1,3,"unsigned int",17);
57728       _cimg_save_pandore_case(0,1,3,"int",17);
57729       _cimg_save_pandore_case(0,1,3,"unsigned int64",17);
57730       _cimg_save_pandore_case(0,1,3,"int64",17);
57731       _cimg_save_pandore_case(0,1,3,"float",18);
57732       _cimg_save_pandore_case(0,1,3,"double",18);
57733 
57734       _cimg_save_pandore_case(0,0,3,"unsigned char",19);
57735       _cimg_save_pandore_case(0,0,3,"char",20);
57736       _cimg_save_pandore_case(0,0,3,"unsigned short",20);
57737       _cimg_save_pandore_case(0,0,3,"short",20);
57738       _cimg_save_pandore_case(0,0,3,"unsigned int",20);
57739       _cimg_save_pandore_case(0,0,3,"int",20);
57740       _cimg_save_pandore_case(0,0,3,"unsigned int64",20);
57741       _cimg_save_pandore_case(0,0,3,"int64",20);
57742       _cimg_save_pandore_case(0,0,3,"float",21);
57743       _cimg_save_pandore_case(0,0,3,"double",21);
57744 
57745       _cimg_save_pandore_case(1,1,0,"unsigned char",22);
57746       _cimg_save_pandore_case(1,1,0,"char",23);
57747       _cimg_save_pandore_case(1,1,0,"unsigned short",23);
57748       _cimg_save_pandore_case(1,1,0,"short",23);
57749       _cimg_save_pandore_case(1,1,0,"unsigned int",23);
57750       _cimg_save_pandore_case(1,1,0,"int",23);
57751       _cimg_save_pandore_case(1,1,0,"unsigned int64",23);
57752       _cimg_save_pandore_case(1,1,0,"int64",23);
57753       _cimg_save_pandore_case(1,1,0,"float",25);
57754       _cimg_save_pandore_case(1,1,0,"double",25);
57755 
57756       _cimg_save_pandore_case(0,1,0,"unsigned char",26);
57757       _cimg_save_pandore_case(0,1,0,"char",27);
57758       _cimg_save_pandore_case(0,1,0,"unsigned short",27);
57759       _cimg_save_pandore_case(0,1,0,"short",27);
57760       _cimg_save_pandore_case(0,1,0,"unsigned int",27);
57761       _cimg_save_pandore_case(0,1,0,"int",27);
57762       _cimg_save_pandore_case(0,1,0,"unsigned int64",27);
57763       _cimg_save_pandore_case(0,1,0,"int64",27);
57764       _cimg_save_pandore_case(0,1,0,"float",29);
57765       _cimg_save_pandore_case(0,1,0,"double",29);
57766 
57767       _cimg_save_pandore_case(0,0,0,"unsigned char",30);
57768       _cimg_save_pandore_case(0,0,0,"char",31);
57769       _cimg_save_pandore_case(0,0,0,"unsigned short",31);
57770       _cimg_save_pandore_case(0,0,0,"short",31);
57771       _cimg_save_pandore_case(0,0,0,"unsigned int",31);
57772       _cimg_save_pandore_case(0,0,0,"int",31);
57773       _cimg_save_pandore_case(0,0,0,"unsigned int64",31);
57774       _cimg_save_pandore_case(0,0,0,"int64",31);
57775       _cimg_save_pandore_case(0,0,0,"float",33);
57776       _cimg_save_pandore_case(0,0,0,"double",33);
57777 
57778       if (!file) cimg::fclose(nfile);
57779       return *this;
57780     }
57781 
57782     //! Save image as a raw data file.
57783     /**
57784        \param filename Filename, as a C-string.
57785        \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
57786        \note The .raw format does not store the image dimensions in the output file,
57787        so you have to keep track of them somewhere to be able to read the file correctly afterwards.
57788     **/
57789     const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
57790       return _save_raw(0,filename,is_multiplexed);
57791     }
57792 
57793     //! Save image as a raw data file \overloading.
57794     /**
57795        Same as save_raw(const char *,bool) const
57796        with a file stream argument instead of a filename string.
57797     **/
57798     const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
57799       return _save_raw(file,0,is_multiplexed);
57800     }
57801 
57802     const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
57803       if (!file && !filename)
57804         throw CImgArgumentException(_cimg_instance
57805                                     "save_raw(): Specified filename is (null).",
57806                                     cimg_instance);
57807       if (is_empty()) { cimg::fempty(file,filename); return *this; }
57808 
57809       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
57810       if (pixel_type()==cimg::type<bool>::string()) { // Boolean data (bitwise)
57811         ulongT siz;
57812         const unsigned char *const buf = _bool2uchar(siz,is_multiplexed);
57813         cimg::fwrite(buf,siz,nfile);
57814         delete[] buf;
57815       } else { // Non boolean data
57816         if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed
57817         else { // Multiplexed
57818           CImg<T> buf(_spectrum);
57819           cimg_forXYZ(*this,x,y,z) {
57820             cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
57821             cimg::fwrite(buf._data,_spectrum,nfile);
57822           }
57823         }
57824       }
57825       if (!file) cimg::fclose(nfile);
57826       return *this;
57827     }
57828 
57829     // Return unsigned char buffer that encodes data of a CImg<bool> instance bitwise.
57830     // (buffer needs to be deallocated afterwards, with delete[]).
57831     const unsigned char *_bool2uchar(ulongT &siz, const bool is_multiplexed) const {
57832       const ulongT _siz = size();
57833       siz = _siz/8 + (_siz%8?1:0);
57834       unsigned char *const buf = new unsigned char[siz], *ptrd = buf, val = 0, bit = 0;
57835 
57836       if (!is_multiplexed || _spectrum==1) // Non-multiplexed
57837         cimg_for(*this,ptrs,T) { (val<<=1)|=(*ptrs?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }}
57838       else // Multiplexed
57839         cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) {
57840           (val<<=1)|=((*this)(x,y,z,c)?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }
57841         }
57842       if (bit) *ptrd = val;
57843       return buf;
57844     }
57845 
57846     // Fill CImg<T> instance from bitwise data encoded in an unsigned char buffer.
57847     void _uchar2bool(const unsigned char *buf, const ulongT siz, const bool is_multiplexed) {
57848       const ulongT S = std::min(siz*8,size());
57849       const unsigned char *ptrs = buf;
57850       unsigned char val = 0, mask = 0;
57851       T *ptrd = _data;
57852       if (S && (!is_multiplexed || _spectrum==1)) // Non-multiplexed
57853         for (ulongT off = 0; off<S; ++off) {
57854           if (!(mask>>=1)) { val = *(ptrs++); mask = 128; }
57855           *(ptrd++) = (T)((val&mask)?1:0);
57856         }
57857       else if (S) { // Multiplexed
57858         ulongT off = 0;
57859         for (int z = 0; z<depth() && off<=S; ++z)
57860           for (int y = 0; y<height() && off<=S; ++y)
57861             for (int x = 0; x<width() && off<=S; ++x)
57862               for (int c = 0; c<spectrum() && off<=S; ++c) {
57863                 if (!(mask>>=1)) { val = *(ptrs++); ++off; mask = 128; }
57864                 (*this)(x,y,z,c) = (T)((val&mask)?1:0);
57865               }
57866       }
57867     }
57868 
57869     //! Save image as a .yuv video file.
57870     /**
57871        \param filename Filename, as a C-string.
57872        \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
57873        \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
57874        \note Each slice of the instance image is considered to be a single frame of the output video file.
57875     **/
57876     const CImg<T>& save_yuv(const char *const filename,
57877                             const unsigned int chroma_subsampling=444,
57878                             const bool is_rgb=true) const {
57879       CImgList<T>(*this,true).save_yuv(filename,chroma_subsampling,is_rgb);
57880       return *this;
57881     }
57882 
57883     //! Save image as a .yuv video file \overloading.
57884     /**
57885        Same as save_yuv(const char*,const unsigned int,const bool) const
57886        with a file stream argument instead of a filename string.
57887     **/
57888     const CImg<T>& save_yuv(std::FILE *const file,
57889                             const unsigned int chroma_subsampling=444,
57890                             const bool is_rgb=true) const {
57891       CImgList<T>(*this,true).save_yuv(file,chroma_subsampling,is_rgb);
57892       return *this;
57893     }
57894 
57895     //! Save 3D object as an Object File Format (.off) file.
57896     /**
57897        \param filename Filename, as a C-string.
57898        \param primitives List of 3D object primitives.
57899        \param colors List of 3D object colors.
57900        \note
57901        - Instance image contains the vertices data of the 3D object.
57902        - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
57903        Such primitives will be lost or simplified during file saving.
57904        - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
57905     **/
57906     template<typename tf, typename tc>
57907     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
57908                             const char *const filename) const {
57909       return _save_off(primitives,colors,0,filename);
57910     }
57911 
57912     //! Save 3D object as an Object File Format (.off) file \overloading.
57913     /**
57914        Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
57915        with a file stream argument instead of a filename string.
57916     **/
57917     template<typename tf, typename tc>
57918     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
57919                             std::FILE *const file) const {
57920       return _save_off(primitives,colors,file,0);
57921     }
57922 
57923     template<typename tf, typename tc>
57924     const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
57925                              std::FILE *const file, const char *const filename) const {
57926       if (!file && !filename)
57927         throw CImgArgumentException(_cimg_instance
57928                                     "save_off(): Specified filename is (null).",
57929                                     cimg_instance);
57930       if (is_empty())
57931         throw CImgInstanceException(_cimg_instance
57932                                     "save_off(): Empty instance, for file '%s'.",
57933                                     cimg_instance,
57934                                     filename?filename:"(FILE*)");
57935 
57936       CImgList<T> opacities;
57937       CImg<charT> error_message(1024);
57938       if (!is_object3d(primitives,colors,opacities,true,error_message))
57939         throw CImgInstanceException(_cimg_instance
57940                                     "save_off(): Invalid specified 3D object, for file '%s' (%s).",
57941                                     cimg_instance,
57942                                     filename?filename:"(FILE*)",error_message.data());
57943 
57944       const CImg<tc> default_color(1,3,1,1,(tc)std::min((int)cimg::type<tc>::max(),200));
57945       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
57946       unsigned int supported_primitives = 0;
57947       cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
57948       std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
57949       cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
57950                                       (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
57951       cimglist_for(primitives,l) {
57952         const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
57953         const unsigned int psiz = primitives[l].size(), csiz = color.size();
57954         const float r = color[0]/255.f, g = (csiz>1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f;
57955         switch (psiz) {
57956         case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
57957                               (unsigned int)primitives(l,0),r,g,b); break;
57958         case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
57959                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
57960         case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
57961                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
57962                               (unsigned int)primitives(l,1),r,g,b); break;
57963         case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
57964                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
57965                               (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
57966         case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
57967                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
57968         case 6 : {
57969           const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
57970           const float
57971             rt = color.atXY(xt,yt,0)/255.f,
57972             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
57973             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
57974           std::fprintf(nfile,"2 %u %u %f %f %f\n",
57975                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
57976         } break;
57977         case 9 : {
57978           const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
57979           const float
57980             rt = color.atXY(xt,yt,0)/255.f,
57981             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
57982             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
57983           std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
57984                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
57985                        (unsigned int)primitives(l,1),rt,gt,bt);
57986         } break;
57987         case 12 : {
57988           const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
57989           const float
57990             rt = color.atXY(xt,yt,0)/255.f,
57991             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
57992             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
57993           std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
57994                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
57995                        (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
57996         } break;
57997         }
57998       }
57999       if (!file) cimg::fclose(nfile);
58000       return *this;
58001     }
58002 
58003     //! Save volumetric image as a video (using the OpenCV library when available).
58004     /**
58005       \param filename Filename to write data to.
58006       \param fps Number of frames per second.
58007       \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
58008       \param keep_open Tells if the video writer associated to the specified filename
58009         must be kept open or not (to allow frames to be added in the same file afterwards).
58010     **/
58011     const CImg<T>& save_video(const char *const filename, const unsigned int fps=25,
58012                               const char *codec=0, const bool keep_open=false) const {
58013       if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; }
58014       CImgList<T> list;
58015       get_split('z').move_to(list);
58016       list.save_video(filename,fps,codec,keep_open);
58017       return *this;
58018     }
58019 
58020     //! Save volumetric image as a video, using ffmpeg external binary.
58021     /**
58022        \param filename Filename, as a C-string.
58023        \param fps Video framerate.
58024        \param codec Video codec, as a C-string.
58025        \param bitrate Video bitrate.
58026        \note
58027        - Each slice of the instance image is considered to be a single frame of the output video file.
58028        - This method uses \c ffmpeg, an external executable binary provided by
58029          <a href="http://www.ffmpeg.org">FFmpeg</a>.
58030        It must be installed for the method to succeed.
58031     **/
58032     const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
58033                                         const char *const codec=0, const unsigned int bitrate=2048) const {
58034       if (!filename)
58035         throw CImgArgumentException(_cimg_instance
58036                                     "save_ffmpeg_external(): Specified filename is (null).",
58037                                     cimg_instance);
58038       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58039 
58040       CImgList<T> list;
58041       get_split('z').move_to(list);
58042       list.save_ffmpeg_external(filename,fps,codec,bitrate);
58043       return *this;
58044     }
58045 
58046     //! Save image using gzip external binary.
58047     /**
58048        \param filename Filename, as a C-string.
58049        \note This method uses \c gzip, an external executable binary provided by
58050          <a href="//http://www.gzip.org">gzip</a>.
58051        It must be installed for the method to succeed.
58052     **/
58053     const CImg<T>& save_gzip_external(const char *const filename) const {
58054       if (!filename)
58055         throw CImgArgumentException(_cimg_instance
58056                                     "save_gzip_external(): Specified filename is (null).",
58057                                     cimg_instance);
58058       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58059 
58060       CImg<charT> command(1024), filename_tmp(256), body(256);
58061       const char
58062         *ext = cimg::split_filename(filename,body),
58063         *ext2 = cimg::split_filename(body,0);
58064       std::FILE *file;
58065       do {
58066         if (!cimg::strcasecmp(ext,"gz")) {
58067           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58068                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
58069           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
58070                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58071         } else {
58072           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58073                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
58074           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
58075                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58076         }
58077         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58078       } while (file);
58079       save(filename_tmp);
58080       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
58081                     cimg::gzip_path(),
58082                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
58083                     CImg<charT>::string(filename)._system_strescape().data());
58084       cimg::system(command, cimg::gzip_path());
58085       file = cimg::std_fopen(filename,"rb");
58086       if (!file)
58087         throw CImgIOException(_cimg_instance
58088                               "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
58089                               cimg_instance,
58090                               filename);
58091 
58092       else cimg::fclose(file);
58093       std::remove(filename_tmp);
58094       return *this;
58095     }
58096 
58097     //! Save image using GraphicsMagick's external binary.
58098     /**
58099        \param filename Filename, as a C-string.
58100        \param quality Image quality (expressed in percent), when the file format supports it.
58101        \note This method uses \c gm, an external executable binary provided by
58102          <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
58103        It must be installed for the method to succeed.
58104     **/
58105     const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
58106       if (!filename)
58107         throw CImgArgumentException(_cimg_instance
58108                                     "save_graphicsmagick_external(): Specified filename is (null).",
58109                                     cimg_instance);
58110       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58111       if (_depth>1)
58112         cimg::warn(_cimg_instance
58113                    "save_other(): File '%s', saving a volumetric image with an external call to "
58114                    "GraphicsMagick only writes the first image slice.",
58115                    cimg_instance,filename);
58116 
58117 #ifdef cimg_use_png
58118 #define _cimg_sge_extension1 "png"
58119 #define _cimg_sge_extension2 "png"
58120 #else
58121 #define _cimg_sge_extension1 "pgm"
58122 #define _cimg_sge_extension2 "ppm"
58123 #endif
58124       CImg<charT> command(1024), filename_tmp(256);
58125       std::FILE *file;
58126       do {
58127         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58128                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
58129                       _spectrum==1?_cimg_sge_extension1:_cimg_sge_extension2);
58130         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58131       } while (file);
58132 
58133 #ifdef cimg_use_png
58134       save_png(filename_tmp);
58135 #else
58136       save_pnm(filename_tmp);
58137 #endif
58138       cimg_snprintf(command,command._width,"\"%s\" convert -quality %u \"%s\" \"%s\"",
58139                     cimg::graphicsmagick_path(),quality,
58140                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
58141                     CImg<charT>::string(filename)._system_strescape().data());
58142       cimg::system(command, cimg::graphicsmagick_path());
58143       file = cimg::std_fopen(filename,"rb");
58144       if (!file)
58145         throw CImgIOException(_cimg_instance
58146                               "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
58147                               cimg_instance,
58148                               filename);
58149 
58150       if (file) cimg::fclose(file);
58151       std::remove(filename_tmp);
58152       return *this;
58153     }
58154 
58155     //! Save image using ImageMagick's external binary.
58156     /**
58157        \param filename Filename, as a C-string.
58158        \param quality Image quality (expressed in percent), when the file format supports it.
58159        \note This method uses \c convert, an external executable binary provided by
58160        <a href="http://www.imagemagick.org">ImageMagick</a>.
58161        It must be installed for the method to succeed.
58162     **/
58163     const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
58164       if (!filename)
58165         throw CImgArgumentException(_cimg_instance
58166                                     "save_imagemagick_external(): Specified filename is (null).",
58167                                     cimg_instance);
58168       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58169       if (_depth>1)
58170         cimg::warn(_cimg_instance
58171                    "save_other(): File '%s', saving a volumetric image with an external call to "
58172                    "ImageMagick only writes the first image slice.",
58173                    cimg_instance,filename);
58174 #ifdef cimg_use_png
58175 #define _cimg_sie_extension1 "png"
58176 #define _cimg_sie_extension2 "png"
58177 #else
58178 #define _cimg_sie_extension1 "pgm"
58179 #define _cimg_sie_extension2 "ppm"
58180 #endif
58181       CImg<charT> command(1024), filename_tmp(256);
58182       std::FILE *file;
58183       do {
58184         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(),
58185                       cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_extension1:_cimg_sie_extension2);
58186         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58187       } while (file);
58188 #ifdef cimg_use_png
58189       save_png(filename_tmp);
58190 #else
58191       save_pnm(filename_tmp);
58192 #endif
58193       cimg_snprintf(command,command._width,"\"%s\" -quality %u \"%s\" \"%s\"",
58194                     cimg::imagemagick_path(),quality,
58195                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
58196                     CImg<charT>::string(filename)._system_strescape().data());
58197       cimg::system(command, cimg::imagemagick_path());
58198       file = cimg::std_fopen(filename,"rb");
58199       if (!file)
58200         throw CImgIOException(_cimg_instance
58201                               "save_imagemagick_external(): Failed to save file '%s' with "
58202                               "external command 'magick/convert'.",
58203                               cimg_instance,
58204                               filename);
58205 
58206       if (file) cimg::fclose(file);
58207       std::remove(filename_tmp);
58208       return *this;
58209     }
58210 
58211     //! Save image as a Dicom file.
58212     /**
58213        \param filename Filename, as a C-string.
58214        \note This method uses \c medcon, an external executable binary provided by
58215          <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
58216        It must be installed for the method to succeed.
58217     **/
58218     const CImg<T>& save_medcon_external(const char *const filename) const {
58219       if (!filename)
58220         throw CImgArgumentException(_cimg_instance
58221                                     "save_medcon_external(): Specified filename is (null).",
58222                                     cimg_instance);
58223       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58224 
58225       CImg<charT> command(1024), filename_tmp(256), body(256);
58226       std::FILE *file;
58227       do {
58228         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
58229         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58230       } while (file);
58231       save_analyze(filename_tmp);
58232       cimg_snprintf(command,command._width,"\"%s\" -w -c dicom -o \"%s\" -f \"%s\"",
58233                     cimg::medcon_path(),
58234                     CImg<charT>::string(filename)._system_strescape().data(),
58235                     CImg<charT>::string(filename_tmp)._system_strescape().data());
58236       cimg::system(command, cimg::medcon_path());
58237       std::remove(filename_tmp);
58238       cimg::split_filename(filename_tmp,body);
58239       cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data);
58240       std::remove(filename_tmp);
58241 
58242       file = cimg::std_fopen(filename,"rb");
58243       if (!file) {
58244         cimg_snprintf(command,command._width,"m000-%s",filename);
58245         file = cimg::std_fopen(command,"rb");
58246         if (!file) {
58247           cimg::fclose(cimg::fopen(filename,"r"));
58248           throw CImgIOException(_cimg_instance
58249                                 "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
58250                                 cimg_instance,
58251                                 filename);
58252         }
58253       }
58254       cimg::fclose(file);
58255       std::rename(command,filename);
58256       return *this;
58257     }
58258 
58259     // Save image for non natively supported formats.
58260     /**
58261        \param filename Filename, as a C-string.
58262        \param quality Image quality (expressed in percent), when the file format supports it.
58263        \note
58264        - The filename extension tells about the desired file format.
58265        - This method tries to save the instance image as a file, using external tools from
58266        <a href="http://www.imagemagick.org">ImageMagick</a> or
58267        <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
58268          At least one of these tool must be installed for the method to succeed.
58269        - It is recommended to use the generic method save(const char*, int) const instead,
58270          as it can handle some file formats natively.
58271     **/
58272     const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
58273       if (!filename)
58274         throw CImgArgumentException(_cimg_instance
58275                                     "save_other(): Specified filename is (null).",
58276                                     cimg_instance);
58277       if (is_empty()) { cimg::fempty(0,filename); return *this; }
58278       if (_depth>1)
58279         cimg::warn(_cimg_instance
58280                    "save_other(): File '%s', saving a volumetric image with an external call to "
58281                    "ImageMagick or GraphicsMagick only writes the first image slice.",
58282                    cimg_instance,filename);
58283 
58284       const unsigned int omode = cimg::exception_mode();
58285       bool is_saved = true;
58286       cimg::exception_mode(0);
58287       try { save_magick(filename); }
58288       catch (CImgException&) {
58289         try { save_imagemagick_external(filename,quality); }
58290         catch (CImgException&) {
58291           try { save_graphicsmagick_external(filename,quality); }
58292           catch (CImgException&) {
58293             is_saved = false;
58294           }
58295         }
58296       }
58297       cimg::exception_mode(omode);
58298       if (!is_saved)
58299         throw CImgIOException(_cimg_instance
58300                               "save_other(): Failed to save file '%s'. Format is not natively supported, "
58301                               "and no external commands succeeded.",
58302                               cimg_instance,
58303                               filename);
58304       return *this;
58305     }
58306 
58307     //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer.
58308     /**
58309        \param is_compressed tells if zlib compression must be used for serialization
58310        (this requires 'cimg_use_zlib' been enabled).
58311     **/
58312     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
58313       return CImgList<T>(*this,true).get_serialize(is_compressed);
58314     }
58315 
58316     // [internal] Return a 40x38 color logo of a 'danger' item.
58317     static CImg<T> _logo40x38() {
58318       CImg<T> res(40,38,1,3);
58319       const unsigned char *ptrs = cimg::logo40x38;
58320       T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
58321       for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
58322         const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
58323         for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
58324       }
58325       return res;
58326     }
58327 
58328     //@}
58329   }; // struct CImg { ...
58330 
58331   /*
58332    #-----------------------------------------
58333    #
58334    #
58335    #
58336    # Definition of the CImgList<T> structure
58337    #
58338    #
58339    #
58340    #------------------------------------------
58341    */
58342   //! Represent a list of images CImg<T>.
58343   template<typename T>
58344   struct CImgList {
58345     unsigned int _width, _allocated_width;
58346     CImg<T> *_data;
58347 
58348     //! Simple iterator type, to loop through each image of a list.
58349     /**
58350        \note
58351        - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
58352        - You may use it like this:
58353        \code
58354        CImgList<> list;   // Assuming this image list is not empty
58355        for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
58356        \endcode
58357        - Using the loop macro \c cimglist_for is another (more concise) alternative:
58358        \code
58359        cimglist_for(list,l) list[l].mirror('x');
58360        \endcode
58361     **/
58362     typedef CImg<T>* iterator;
58363 
58364     //! Simple const iterator type, to loop through each image of a \c const list instance.
58365     /**
58366        \note
58367        - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
58368        - Similar to CImgList<T>::iterator, but for constant list instances.
58369     **/
58370     typedef const CImg<T>* const_iterator;
58371 
58372     //! Pixel value type.
58373     /**
58374        Refer to the pixels value type of the images in the list.
58375        \note
58376        - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T.
58377          It is then similar to CImg<T>::value_type.
58378        - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
58379          compatibility with STL naming conventions.
58380     **/
58381     typedef T value_type;
58382 
58383     // Define common types related to template type T.
58384     typedef typename cimg::superset<T,bool>::type Tbool;
58385     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
58386     typedef typename cimg::superset<T,char>::type Tchar;
58387     typedef typename cimg::superset<T,unsigned short>::type Tushort;
58388     typedef typename cimg::superset<T,short>::type Tshort;
58389     typedef typename cimg::superset<T,unsigned int>::type Tuint;
58390     typedef typename cimg::superset<T,int>::type Tint;
58391     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
58392     typedef typename cimg::superset<T,cimg_long>::type Tlong;
58393     typedef typename cimg::superset<T,float>::type Tfloat;
58394     typedef typename cimg::superset<T,double>::type Tdouble;
58395     typedef typename cimg::last<T,bool>::type boolT;
58396     typedef typename cimg::last<T,unsigned char>::type ucharT;
58397     typedef typename cimg::last<T,char>::type charT;
58398     typedef typename cimg::last<T,unsigned short>::type ushortT;
58399     typedef typename cimg::last<T,short>::type shortT;
58400     typedef typename cimg::last<T,unsigned int>::type uintT;
58401     typedef typename cimg::last<T,int>::type intT;
58402     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
58403     typedef typename cimg::last<T,cimg_long>::type longT;
58404     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
58405     typedef typename cimg::last<T,cimg_int64>::type int64T;
58406     typedef typename cimg::last<T,float>::type floatT;
58407     typedef typename cimg::last<T,double>::type doubleT;
58408 
58409     //@}
58410     //---------------------------
58411     //
58412     //! \name Plugins
58413     //@{
58414     //---------------------------
58415 #ifdef cimglist_plugin
58416 #include cimglist_plugin
58417 #endif
58418 #ifdef cimglist_plugin1
58419 #include cimglist_plugin1
58420 #endif
58421 #ifdef cimglist_plugin2
58422 #include cimglist_plugin2
58423 #endif
58424 #ifdef cimglist_plugin3
58425 #include cimglist_plugin3
58426 #endif
58427 #ifdef cimglist_plugin4
58428 #include cimglist_plugin4
58429 #endif
58430 #ifdef cimglist_plugin5
58431 #include cimglist_plugin5
58432 #endif
58433 #ifdef cimglist_plugin6
58434 #include cimglist_plugin6
58435 #endif
58436 #ifdef cimglist_plugin7
58437 #include cimglist_plugin7
58438 #endif
58439 #ifdef cimglist_plugin8
58440 #include cimglist_plugin8
58441 #endif
58442 
58443     //@}
58444     //--------------------------------------------------------
58445     //
58446     //! \name Constructors / Destructor / Instance Management
58447     //@{
58448     //--------------------------------------------------------
58449 
58450     //! Destructor.
58451     /**
58452        Destroy current list instance.
58453        \note
58454        - Any allocated buffer is deallocated.
58455        - Destroying an empty list does nothing actually.
58456      **/
58457     ~CImgList() {
58458       delete[] _data;
58459     }
58460 
58461     //! Default constructor.
58462     /**
58463        Construct a new empty list instance.
58464        \note
58465        - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its
58466          image buffer pointer data().
58467        - An empty list may be reassigned afterwards, with the family of the assign() methods.
58468          In all cases, the type of pixels stays \c T.
58469      **/
58470     CImgList():
58471       _width(0),_allocated_width(0),_data(0) {}
58472 
58473     //! Construct list containing empty images.
58474     /**
58475        \param n Number of empty images.
58476        \note Useful when you know by advance the number of images you want to manage, as
58477        it will allocate the right amount of memory for the list, without needs for reallocation
58478        (that may occur when starting from an empty list and inserting several images in it).
58479     **/
58480     explicit CImgList(const unsigned int n):_width(n) {
58481       if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
58482       else { _allocated_width = 0; _data = 0; }
58483     }
58484 
58485     //! Construct list containing images of specified size.
58486     /**
58487        \param n Number of images.
58488        \param width Width of images.
58489        \param height Height of images.
58490        \param depth Depth of images.
58491        \param spectrum Number of channels of images.
58492        \note Pixel values are not initialized and may probably contain garbage.
58493     **/
58494     CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
58495              const unsigned int depth=1, const unsigned int spectrum=1):
58496       _width(0),_allocated_width(0),_data(0) {
58497       assign(n);
58498       cimglist_apply(*this,assign)(width,height,depth,spectrum);
58499     }
58500 
58501     //! Construct list containing images of specified size, and initialize pixel values.
58502     /**
58503        \param n Number of images.
58504        \param width Width of images.
58505        \param height Height of images.
58506        \param depth Depth of images.
58507        \param spectrum Number of channels of images.
58508        \param val Initialization value for images pixels.
58509     **/
58510     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
58511              const unsigned int depth, const unsigned int spectrum, const T& val):
58512       _width(0),_allocated_width(0),_data(0) {
58513       assign(n);
58514       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
58515     }
58516 
58517     //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
58518     /**
58519        \param n Number of images.
58520        \param width Width of images.
58521        \param height Height of images.
58522        \param depth Depth of images.
58523        \param spectrum Number of channels of images.
58524        \param val0 First value of the initializing integers sequence.
58525        \param val1 Second value of the initializing integers sequence.
58526        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
58527          or you will probably segfault.
58528     **/
58529     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
58530              const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
58531       _width(0),_allocated_width(0),_data(0) {
58532 #define _CImgList_stdarg(t) { \
58533         assign(n,width,height,depth,spectrum); \
58534         const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \
58535         T *ptrd = _data->_data; \
58536         va_list ap; \
58537         va_start(ap,val1); \
58538         for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \
58539           *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
58540           if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
58541         } \
58542         va_end(ap); \
58543       }
58544       _CImgList_stdarg(int);
58545     }
58546 
58547     //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
58548     /**
58549        \param n Number of images.
58550        \param width Width of images.
58551        \param height Height of images.
58552        \param depth Depth of images.
58553        \param spectrum Number of channels of images.
58554        \param val0 First value of the initializing doubles sequence.
58555        \param val1 Second value of the initializing doubles sequence.
58556        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
58557          or you will probably segfault.
58558     **/
58559     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
58560              const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
58561       _width(0),_allocated_width(0),_data(0) {
58562       _CImgList_stdarg(double);
58563     }
58564 
58565     //! Construct list containing copies of an input image.
58566     /**
58567        \param n Number of images.
58568        \param img Input image to copy in the constructed list.
58569        \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
58570     **/
58571     template<typename t>
58572     CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
58573       _width(0),_allocated_width(0),_data(0) {
58574       assign(n);
58575       cimglist_apply(*this,assign)(img,is_shared);
58576     }
58577 
58578     //! Construct list from one image.
58579     /**
58580        \param img Input image to copy in the constructed list.
58581        \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
58582      **/
58583     template<typename t>
58584     explicit CImgList(const CImg<t>& img, const bool is_shared=false):
58585       _width(0),_allocated_width(0),_data(0) {
58586       assign(1);
58587       _data[0].assign(img,is_shared);
58588     }
58589 
58590     //! Construct list from two images.
58591     /**
58592        \param img1 First input image to copy in the constructed list.
58593        \param img2 Second input image to copy in the constructed list.
58594        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
58595      **/
58596     template<typename t1, typename t2>
58597     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
58598       _width(0),_allocated_width(0),_data(0) {
58599       assign(2);
58600       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
58601     }
58602 
58603     //! Construct list from three images.
58604     /**
58605        \param img1 First input image to copy in the constructed list.
58606        \param img2 Second input image to copy in the constructed list.
58607        \param img3 Third input image to copy in the constructed list.
58608        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
58609     **/
58610     template<typename t1, typename t2, typename t3>
58611     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
58612       _width(0),_allocated_width(0),_data(0) {
58613       assign(3);
58614       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58615     }
58616 
58617     //! Construct list from four images.
58618     /**
58619        \param img1 First input image to copy in the constructed list.
58620        \param img2 Second input image to copy in the constructed list.
58621        \param img3 Third input image to copy in the constructed list.
58622        \param img4 Fourth input image to copy in the constructed list.
58623        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
58624     **/
58625     template<typename t1, typename t2, typename t3, typename t4>
58626     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58627              const bool is_shared=false):
58628       _width(0),_allocated_width(0),_data(0) {
58629       assign(4);
58630       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58631       _data[3].assign(img4,is_shared);
58632     }
58633 
58634     //! Construct list from five images.
58635     /**
58636        \param img1 First input image to copy in the constructed list.
58637        \param img2 Second input image to copy in the constructed list.
58638        \param img3 Third input image to copy in the constructed list.
58639        \param img4 Fourth input image to copy in the constructed list.
58640        \param img5 Fifth input image to copy in the constructed list.
58641        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
58642     **/
58643     template<typename t1, typename t2, typename t3, typename t4, typename t5>
58644     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58645              const CImg<t5>& img5, const bool is_shared=false):
58646       _width(0),_allocated_width(0),_data(0) {
58647       assign(5);
58648       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58649       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
58650     }
58651 
58652     //! Construct list from six images.
58653     /**
58654        \param img1 First input image to copy in the constructed list.
58655        \param img2 Second input image to copy in the constructed list.
58656        \param img3 Third input image to copy in the constructed list.
58657        \param img4 Fourth input image to copy in the constructed list.
58658        \param img5 Fifth input image to copy in the constructed list.
58659        \param img6 Sixth input image to copy in the constructed list.
58660        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
58661     **/
58662     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
58663     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58664              const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
58665       _width(0),_allocated_width(0),_data(0) {
58666       assign(6);
58667       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58668       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
58669     }
58670 
58671     //! Construct list from seven images.
58672     /**
58673        \param img1 First input image to copy in the constructed list.
58674        \param img2 Second input image to copy in the constructed list.
58675        \param img3 Third input image to copy in the constructed list.
58676        \param img4 Fourth input image to copy in the constructed list.
58677        \param img5 Fifth input image to copy in the constructed list.
58678        \param img6 Sixth input image to copy in the constructed list.
58679        \param img7 Seventh input image to copy in the constructed list.
58680        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
58681     **/
58682     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
58683     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58684              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
58685       _width(0),_allocated_width(0),_data(0) {
58686       assign(7);
58687       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58688       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
58689       _data[6].assign(img7,is_shared);
58690     }
58691 
58692     //! Construct list from eight images.
58693     /**
58694        \param img1 First input image to copy in the constructed list.
58695        \param img2 Second input image to copy in the constructed list.
58696        \param img3 Third input image to copy in the constructed list.
58697        \param img4 Fourth input image to copy in the constructed list.
58698        \param img5 Fifth input image to copy in the constructed list.
58699        \param img6 Sixth input image to copy in the constructed list.
58700        \param img7 Seventh input image to copy in the constructed list.
58701        \param img8 Eighth input image to copy in the constructed list.
58702        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
58703     **/
58704     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
58705     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58706              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
58707              const bool is_shared=false):
58708       _width(0),_allocated_width(0),_data(0) {
58709       assign(8);
58710       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58711       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
58712       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
58713     }
58714 
58715     //! Construct list copy.
58716     /**
58717        \param list Input list to copy.
58718        \note The shared state of each element of the constructed list is kept the same as in \c list.
58719     **/
58720     template<typename t>
58721     CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
58722       assign(list._width);
58723       cimglist_for(*this,l) _data[l].assign(list[l],false);
58724     }
58725 
58726     //! Construct list copy \specialization.
58727     CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
58728       assign(list._width);
58729       cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
58730     }
58731 
58732     //! Construct list copy, and force the shared state of the list elements.
58733     /**
58734        \param list Input list to copy.
58735        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
58736     **/
58737     template<typename t>
58738     CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
58739       assign(list._width);
58740       cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
58741     }
58742 
58743     //! Construct list by reading the content of a file.
58744     /**
58745        \param filename Filename, as a C-string.
58746     **/
58747     explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
58748       assign(filename);
58749     }
58750 
58751     //! Construct list from the content of a display window.
58752     /**
58753        \param disp Display window to get content from.
58754        \note Constructed list contains a single image only.
58755     **/
58756     explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
58757       assign(disp);
58758     }
58759 
58760     //! Return a list with elements being shared copies of images in the list instance.
58761     /**
58762       \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
58763     **/
58764     CImgList<T> get_shared() {
58765       CImgList<T> res(_width);
58766       cimglist_for(*this,l) res[l].assign(_data[l],true);
58767       return res;
58768     }
58769 
58770     //! Return a list with elements being shared copies of images in the list instance \const.
58771     const CImgList<T> get_shared() const {
58772       CImgList<T> res(_width);
58773       cimglist_for(*this,l) res[l].assign(_data[l],true);
58774       return res;
58775     }
58776 
58777     //! Destructor \inplace.
58778     /**
58779        \see CImgList().
58780     **/
58781     CImgList<T>& assign() {
58782       delete[] _data;
58783       _width = _allocated_width = 0;
58784       _data = 0;
58785       return *this;
58786     }
58787 
58788     //! Destructor \inplace.
58789     /**
58790        Equivalent to assign().
58791        \note Only here for compatibility with STL naming conventions.
58792     **/
58793     CImgList<T>& clear() {
58794       return assign();
58795     }
58796 
58797     //! Construct list containing empty images \inplace.
58798     /**
58799        \see CImgList(unsigned int).
58800     **/
58801     CImgList<T>& assign(const unsigned int n) {
58802       if (!n) return assign();
58803       if (_allocated_width<n || _allocated_width>(n<<2)) {
58804         delete[] _data;
58805         _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
58806       }
58807       _width = n;
58808       return *this;
58809     }
58810 
58811     //! Construct list containing images of specified size \inplace.
58812     /**
58813        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
58814     **/
58815     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
58816                         const unsigned int depth=1, const unsigned int spectrum=1) {
58817       assign(n);
58818       cimglist_apply(*this,assign)(width,height,depth,spectrum);
58819       return *this;
58820     }
58821 
58822     //! Construct list containing images of specified size, and initialize pixel values \inplace.
58823     /**
58824        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
58825     **/
58826     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
58827                         const unsigned int depth, const unsigned int spectrum, const T& val) {
58828       assign(n);
58829       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
58830       return *this;
58831     }
58832 
58833     //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace.
58834     /**
58835        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
58836     **/
58837     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
58838                         const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
58839       _CImgList_stdarg(int);
58840       return *this;
58841     }
58842 
58843     //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace.
58844     /**
58845        \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...).
58846     **/
58847     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
58848                         const unsigned int depth, const unsigned int spectrum,
58849                         const double val0, const double val1, ...) {
58850       _CImgList_stdarg(double);
58851       return *this;
58852     }
58853 
58854     //! Construct list containing copies of an input image \inplace.
58855     /**
58856        \see CImgList(unsigned int, const CImg<t>&, bool).
58857     **/
58858     template<typename t>
58859     CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
58860       assign(n);
58861       cimglist_apply(*this,assign)(img,is_shared);
58862       return *this;
58863     }
58864 
58865     //! Construct list from one image \inplace.
58866     /**
58867        \see CImgList(const CImg<t>&, bool).
58868     **/
58869     template<typename t>
58870     CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
58871       assign(1);
58872       _data[0].assign(img,is_shared);
58873       return *this;
58874     }
58875 
58876     //! Construct list from two images \inplace.
58877     /**
58878        \see CImgList(const CImg<t>&, const CImg<t>&, bool).
58879     **/
58880     template<typename t1, typename t2>
58881     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
58882       assign(2);
58883       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
58884       return *this;
58885     }
58886 
58887     //! Construct list from three images \inplace.
58888     /**
58889        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
58890     **/
58891     template<typename t1, typename t2, typename t3>
58892     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
58893       assign(3);
58894       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58895       return *this;
58896     }
58897 
58898     //! Construct list from four images \inplace.
58899     /**
58900        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
58901     **/
58902     template<typename t1, typename t2, typename t3, typename t4>
58903     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58904                         const bool is_shared=false) {
58905       assign(4);
58906       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58907       _data[3].assign(img4,is_shared);
58908       return *this;
58909     }
58910 
58911     //! Construct list from five images \inplace.
58912     /**
58913        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
58914     **/
58915     template<typename t1, typename t2, typename t3, typename t4, typename t5>
58916     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58917                         const CImg<t5>& img5, const bool is_shared=false) {
58918       assign(5);
58919       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58920       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
58921       return *this;
58922     }
58923 
58924     //! Construct list from six images \inplace.
58925     /**
58926        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool).
58927     **/
58928     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
58929     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58930                         const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
58931       assign(6);
58932       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58933       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
58934       return *this;
58935     }
58936 
58937     //! Construct list from seven images \inplace.
58938     /**
58939        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
58940        const CImg<t>&, bool).
58941     **/
58942     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
58943     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58944                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
58945       assign(7);
58946       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58947       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
58948       _data[6].assign(img7,is_shared);
58949       return *this;
58950     }
58951 
58952     //! Construct list from eight images \inplace.
58953     /**
58954        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
58955        const CImg<t>&, const CImg<t>&, bool).
58956     **/
58957     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
58958     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
58959                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
58960                         const bool is_shared=false) {
58961       assign(8);
58962       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
58963       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
58964       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
58965       return *this;
58966     }
58967 
58968     //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
58969     /**
58970       \see CImgList(const CImgList<t>&, bool is_shared).
58971     **/
58972     template<typename t>
58973     CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
58974       cimg::unused(is_shared);
58975       assign(list._width);
58976       cimglist_for(*this,l) _data[l].assign(list[l],false);
58977       return *this;
58978     }
58979 
58980     //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization.
58981     CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
58982       if (this==&list) return *this;
58983       CImgList<T> res(list._width);
58984       cimglist_for(res,l) res[l].assign(list[l],is_shared);
58985       return res.move_to(*this);
58986     }
58987 
58988     //! Construct list by reading the content of a file \inplace.
58989     /**
58990       \see CImgList(const char *const).
58991     **/
58992     CImgList<T>& assign(const char *const filename) {
58993       return load(filename);
58994     }
58995 
58996     //! Construct list from the content of a display window \inplace.
58997     /**
58998       \see CImgList(const CImgDisplay&).
58999     **/
59000     CImgList<T>& assign(const CImgDisplay &disp) {
59001       return assign(CImg<T>(disp));
59002     }
59003 
59004     //! Transfer the content of the list instance to another list.
59005     /**
59006        \param list Destination list.
59007        \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
59008     **/
59009     template<typename t>
59010     CImgList<t>& move_to(CImgList<t>& list) {
59011       list.assign(_width);
59012       bool is_one_shared_element = false;
59013       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
59014       if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
59015       else cimglist_for(*this,l) _data[l].move_to(list[l]);
59016       assign();
59017       return list;
59018     }
59019 
59020     //! Transfer the content of the list instance at a specified position in another list.
59021     /**
59022        \param list Destination list.
59023        \param pos Index of the insertion in the list.
59024        \note When returning, the list instance is empty and the initial content of \c list is preserved
59025        (only images indexes may be modified).
59026      **/
59027     template<typename t>
59028     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
59029       if (is_empty()) return list;
59030       const unsigned int npos = pos>list._width?list._width:pos;
59031       list.insert(_width,npos);
59032       bool is_one_shared_element = false;
59033       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
59034       if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]);
59035       else cimglist_for(*this,l) _data[l].move_to(list[npos + l]);
59036       assign();
59037       return list;
59038     }
59039 
59040     //! Swap all fields between two list instances.
59041     /**
59042        \param list List to swap fields with.
59043        \note Can be used to exchange the content of two lists in a fast way.
59044     **/
59045     CImgList<T>& swap(CImgList<T>& list) {
59046       cimg::swap(_width,list._width,_allocated_width,list._allocated_width);
59047       cimg::swap(_data,list._data);
59048       return list;
59049     }
59050 
59051     //! Return a reference to an empty list.
59052     /**
59053       \note Can be used to define default values in a function taking a CImgList<T> as an argument.
59054       \code
59055       void f(const CImgList<char>& list=CImgList<char>::empty());
59056       \endcode
59057     **/
59058     static CImgList<T>& empty() {
59059       static CImgList<T> _empty;
59060       return _empty.assign();
59061     }
59062 
59063     //! Return a reference to an empty list \const.
59064     static const CImgList<T>& const_empty() {
59065       static const CImgList<T> _empty;
59066       return _empty;
59067     }
59068 
59069     //@}
59070     //------------------------------------------
59071     //
59072     //! \name Overloaded Operators
59073     //@{
59074     //------------------------------------------
59075 
59076     //! Return a reference to one image element of the list.
59077     /**
59078        \param pos Index of the image element.
59079     **/
59080     CImg<T>& operator()(const unsigned int pos) {
59081 #if cimg_verbosity>=3
59082       if (pos>=_width) {
59083         cimg::warn(_cimglist_instance
59084                    "operator(): Invalid image request, at position [%u].",
59085                    cimglist_instance,
59086                    pos);
59087         return *_data;
59088       }
59089 #endif
59090       return _data[pos];
59091     }
59092 
59093     //! Return a reference to one image of the list.
59094     /**
59095        \param pos Index of the image element.
59096     **/
59097     const CImg<T>& operator()(const unsigned int pos) const {
59098       return const_cast<CImgList<T>*>(this)->operator()(pos);
59099     }
59100 
59101     //! Return a reference to one pixel value of one image of the list.
59102     /**
59103        \param pos Index of the image element.
59104        \param x X-coordinate of the pixel value.
59105        \param y Y-coordinate of the pixel value.
59106        \param z Z-coordinate of the pixel value.
59107        \param c C-coordinate of the pixel value.
59108        \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
59109     **/
59110     T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
59111                   const unsigned int z=0, const unsigned int c=0) {
59112       return (*this)[pos](x,y,z,c);
59113     }
59114 
59115     //! Return a reference to one pixel value of one image of the list \const.
59116     const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
59117                         const unsigned int z=0, const unsigned int c=0) const {
59118       return (*this)[pos](x,y,z,c);
59119     }
59120 
59121     //! Return pointer to the first image of the list.
59122     /**
59123        \note Images in a list are stored as a buffer of \c CImg<T>.
59124     **/
59125     operator CImg<T>*() {
59126       return _data;
59127     }
59128 
59129     //! Return pointer to the first image of the list \const.
59130     operator const CImg<T>*() const {
59131       return _data;
59132     }
59133 
59134     //! Construct list from one image \inplace.
59135     /**
59136         \param img Input image to copy in the constructed list.
59137         \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
59138     **/
59139     template<typename t>
59140     CImgList<T>& operator=(const CImg<t>& img) {
59141       return assign(img);
59142     }
59143 
59144     //! Construct list from another list.
59145     /**
59146        \param list Input list to copy.
59147        \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
59148     **/
59149     template<typename t>
59150     CImgList<T>& operator=(const CImgList<t>& list) {
59151       return assign(list);
59152     }
59153 
59154     //! Construct list from another list \specialization.
59155     CImgList<T>& operator=(const CImgList<T>& list) {
59156       return assign(list);
59157     }
59158 
59159     //! Construct list by reading the content of a file \inplace.
59160     /**
59161        \see CImgList(const char *const).
59162     **/
59163     CImgList<T>& operator=(const char *const filename) {
59164       return assign(filename);
59165     }
59166 
59167     //! Construct list from the content of a display window \inplace.
59168     /**
59169         \see CImgList(const CImgDisplay&).
59170     **/
59171     CImgList<T>& operator=(const CImgDisplay& disp) {
59172       return assign(disp);
59173     }
59174 
59175     //! Return a non-shared copy of a list.
59176     /**
59177         \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>.
59178           It forces the copy to have non-shared elements.
59179     **/
59180     CImgList<T> operator+() const {
59181       return CImgList<T>(*this,false);
59182     }
59183 
59184     //! Return a copy of the list instance, where image \c img has been inserted at the end.
59185     /**
59186        \param img Image inserted at the end of the instance copy.
59187        \note Define a convenient way to create temporary lists of images, as in the following code:
59188        \code
59189        (img1,img2,img3,img4).display("My four images");
59190        \endcode
59191     **/
59192     template<typename t>
59193     CImgList<T>& operator,(const CImg<t>& img) {
59194       return insert(img);
59195     }
59196 
59197     //! Return a copy of the list instance, where image \c img has been inserted at the end \const.
59198     template<typename t>
59199     CImgList<T> operator,(const CImg<t>& img) const {
59200       return (+*this).insert(img);
59201     }
59202 
59203     //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
59204     /**
59205        \param list List inserted at the end of the instance copy.
59206     **/
59207     template<typename t>
59208     CImgList<T>& operator,(const CImgList<t>& list) {
59209       return insert(list);
59210     }
59211 
59212     //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const.
59213     template<typename t>
59214     CImgList<T>& operator,(const CImgList<t>& list) const {
59215       return (+*this).insert(list);
59216     }
59217 
59218     //! Return image corresponding to the appending of all images of the instance list along specified axis.
59219     /**
59220       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
59221       \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
59222     **/
59223     CImg<T> operator>(const char axis) const {
59224       return get_append(axis,0);
59225     }
59226 
59227     //! Return list corresponding to the splitting of all images of the instance list along specified axis.
59228     /**
59229       \param axis Axis used for image splitting.
59230       \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
59231     **/
59232     CImgList<T> operator<(const char axis) const {
59233       return get_split(axis);
59234     }
59235 
59236     //@}
59237     //-------------------------------------
59238     //
59239     //! \name Instance Characteristics
59240     //@{
59241     //-------------------------------------
59242 
59243     //! Return the type of image pixel values as a C string.
59244     /**
59245        Return a \c char* string containing the usual type name of the image pixel values
59246        (i.e. a stringified version of the template parameter \c T).
59247        \note
59248        - The returned string may contain spaces (as in \c "unsigned char").
59249        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
59250     **/
59251     static const char* pixel_type() {
59252       return cimg::type<T>::string();
59253     }
59254 
59255     //! Return the size of the list, i.e. the number of images contained in it.
59256     /**
59257       \note Similar to size() but returns result as a (signed) integer.
59258     **/
59259     int width() const {
59260       return (int)_width;
59261     }
59262 
59263     //! Return the size of the list, i.e. the number of images contained in it.
59264     /**
59265       \note Similar to width() but returns result as an unsigned integer.
59266     **/
59267     unsigned int size() const {
59268       return _width;
59269     }
59270 
59271     //! Return pointer to the first image of the list.
59272     /**
59273        \note Images in a list are stored as a buffer of \c CImg<T>.
59274     **/
59275     CImg<T> *data() {
59276       return _data;
59277     }
59278 
59279     //! Return pointer to the first image of the list \const.
59280     const CImg<T> *data() const {
59281       return _data;
59282     }
59283 
59284     //! Return pointer to the pos-th image of the list.
59285     /**
59286        \param pos Index of the image element to access.
59287        \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
59288     **/
59289 #if cimg_verbosity>=3
59290     CImg<T> *data(const unsigned int pos) {
59291       if (pos>=size())
59292         cimg::warn(_cimglist_instance
59293                    "data(): Invalid pointer request, at position [%u].",
59294                    cimglist_instance,
59295                    pos);
59296       return _data + pos;
59297     }
59298 
59299     const CImg<T> *data(const unsigned int l) const {
59300       return const_cast<CImgList<T>*>(this)->data(l);
59301     }
59302 #else
59303     CImg<T> *data(const unsigned int l) {
59304       return _data + l;
59305     }
59306 
59307     //! Return pointer to the pos-th image of the list \const.
59308     const CImg<T> *data(const unsigned int l) const {
59309       return _data + l;
59310     }
59311 #endif
59312 
59313     //! Return iterator to the first image of the list.
59314     /**
59315     **/
59316     iterator begin() {
59317       return _data;
59318     }
59319 
59320     //! Return iterator to the first image of the list \const.
59321     const_iterator begin() const {
59322       return _data;
59323     }
59324 
59325     //! Return iterator to one position after the last image of the list.
59326     /**
59327     **/
59328     iterator end() {
59329       return _data + _width;
59330     }
59331 
59332     //! Return iterator to one position after the last image of the list \const.
59333     const_iterator end() const {
59334       return _data + _width;
59335     }
59336 
59337     //! Return reference to the first image of the list.
59338     /**
59339     **/
59340     CImg<T>& front() {
59341       return *_data;
59342     }
59343 
59344     //! Return reference to the first image of the list \const.
59345     const CImg<T>& front() const {
59346       return *_data;
59347     }
59348 
59349     //! Return a reference to the last image of the list.
59350     /**
59351     **/
59352     const CImg<T>& back() const {
59353       return *(_data + _width - 1);
59354     }
59355 
59356     //! Return a reference to the last image of the list \const.
59357     CImg<T>& back() {
59358       return *(_data + _width - 1);
59359     }
59360 
59361     //! Return pos-th image of the list.
59362     /**
59363        \param pos Index of the image element to access.
59364     **/
59365     CImg<T>& at(const int pos) {
59366       if (is_empty())
59367         throw CImgInstanceException(_cimglist_instance
59368                                     "at(): Empty instance.",
59369                                     cimglist_instance);
59370 
59371       return _data[cimg::cut(pos,0,width() - 1)];
59372     }
59373 
59374     //! Access to pixel value with Dirichlet boundary conditions.
59375     /**
59376        \param pos Index of the image element to access.
59377        \param x X-coordinate of the pixel value.
59378        \param y Y-coordinate of the pixel value.
59379        \param z Z-coordinate of the pixel value.
59380        \param c C-coordinate of the pixel value.
59381        \param out_value Default value returned if \c offset is outside image bounds.
59382        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
59383     **/
59384     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
59385       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
59386     }
59387 
59388     //! Access to pixel value with Dirichlet boundary conditions \const.
59389     T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
59390       return (pos<0 || pos>=width())?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
59391     }
59392 
59393     //! Access to pixel value with Neumann boundary conditions.
59394     /**
59395        \param pos Index of the image element to access.
59396        \param x X-coordinate of the pixel value.
59397        \param y Y-coordinate of the pixel value.
59398        \param z Z-coordinate of the pixel value.
59399        \param c C-coordinate of the pixel value.
59400        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
59401     **/
59402     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
59403       if (is_empty())
59404         throw CImgInstanceException(_cimglist_instance
59405                                     "atNXYZC(): Empty instance.",
59406                                     cimglist_instance);
59407 
59408       return _atNXYZC(pos,x,y,z,c);
59409     }
59410 
59411     //! Access to pixel value with Neumann boundary conditions \const.
59412     T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
59413       if (is_empty())
59414         throw CImgInstanceException(_cimglist_instance
59415                                     "atNXYZC(): Empty instance.",
59416                                     cimglist_instance);
59417 
59418       return _atNXYZC(pos,x,y,z,c);
59419     }
59420 
59421     T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
59422       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
59423     }
59424 
59425     T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
59426       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
59427     }
59428 
59429     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z).
59430     /**
59431        \param pos Index of the image element to access.
59432        \param x X-coordinate of the pixel value.
59433        \param y Y-coordinate of the pixel value.
59434        \param z Z-coordinate of the pixel value.
59435        \param c C-coordinate of the pixel value.
59436        \param out_value Default value returned if \c offset is outside image bounds.
59437        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
59438     **/
59439     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
59440       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
59441     }
59442 
59443     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const.
59444     T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
59445       return (pos<0 || pos>=width())?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
59446     }
59447 
59448     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z).
59449     /**
59450        \param pos Index of the image element to access.
59451        \param x X-coordinate of the pixel value.
59452        \param y Y-coordinate of the pixel value.
59453        \param z Z-coordinate of the pixel value.
59454        \param c C-coordinate of the pixel value.
59455        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
59456     **/
59457    T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
59458       if (is_empty())
59459         throw CImgInstanceException(_cimglist_instance
59460                                     "atNXYZ(): Empty instance.",
59461                                     cimglist_instance);
59462 
59463       return _atNXYZ(pos,x,y,z,c);
59464     }
59465 
59466     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const.
59467     T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
59468       if (is_empty())
59469         throw CImgInstanceException(_cimglist_instance
59470                                     "atNXYZ(): Empty instance.",
59471                                     cimglist_instance);
59472 
59473       return _atNXYZ(pos,x,y,z,c);
59474     }
59475 
59476     T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
59477       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
59478     }
59479 
59480     T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
59481       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
59482     }
59483 
59484     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
59485     /**
59486        \param pos Index of the image element to access.
59487        \param x X-coordinate of the pixel value.
59488        \param y Y-coordinate of the pixel value.
59489        \param z Z-coordinate of the pixel value.
59490        \param c C-coordinate of the pixel value.
59491        \param out_value Default value returned if \c offset is outside image bounds.
59492        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
59493     **/
59494     T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
59495       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
59496     }
59497 
59498     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
59499     T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
59500       return (pos<0 || pos>=width())?out_value:_data[pos].atXY(x,y,z,c,out_value);
59501     }
59502 
59503     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
59504     /**
59505        \param pos Index of the image element to access.
59506        \param x X-coordinate of the pixel value.
59507        \param y Y-coordinate of the pixel value.
59508        \param z Z-coordinate of the pixel value.
59509        \param c C-coordinate of the pixel value.
59510        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
59511     **/
59512     T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
59513       if (is_empty())
59514         throw CImgInstanceException(_cimglist_instance
59515                                     "atNXY(): Empty instance.",
59516                                     cimglist_instance);
59517 
59518       return _atNXY(pos,x,y,z,c);
59519     }
59520 
59521     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
59522     T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
59523       if (is_empty())
59524         throw CImgInstanceException(_cimglist_instance
59525                                     "atNXY(): Empty instance.",
59526                                     cimglist_instance);
59527 
59528       return _atNXY(pos,x,y,z,c);
59529     }
59530 
59531     T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
59532       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
59533     }
59534 
59535     T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
59536       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
59537     }
59538 
59539     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x).
59540     /**
59541        \param pos Index of the image element to access.
59542        \param x X-coordinate of the pixel value.
59543        \param y Y-coordinate of the pixel value.
59544        \param z Z-coordinate of the pixel value.
59545        \param c C-coordinate of the pixel value.
59546        \param out_value Default value returned if \c offset is outside image bounds.
59547        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
59548     **/
59549     T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
59550       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
59551     }
59552 
59553     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const.
59554     T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
59555       return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value);
59556     }
59557 
59558     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x).
59559     /**
59560        \param pos Index of the image element to access.
59561        \param x X-coordinate of the pixel value.
59562        \param y Y-coordinate of the pixel value.
59563        \param z Z-coordinate of the pixel value.
59564        \param c C-coordinate of the pixel value.
59565        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
59566     **/
59567     T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
59568       if (is_empty())
59569         throw CImgInstanceException(_cimglist_instance
59570                                     "atNX(): Empty instance.",
59571                                     cimglist_instance);
59572 
59573       return _atNX(pos,x,y,z,c);
59574     }
59575 
59576     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const.
59577     T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
59578       if (is_empty())
59579         throw CImgInstanceException(_cimglist_instance
59580                                     "atNX(): Empty instance.",
59581                                     cimglist_instance);
59582 
59583       return _atNX(pos,x,y,z,c);
59584     }
59585 
59586     T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
59587       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
59588     }
59589 
59590     T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
59591       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
59592     }
59593 
59594     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos).
59595     /**
59596        \param pos Index of the image element to access.
59597        \param x X-coordinate of the pixel value.
59598        \param y Y-coordinate of the pixel value.
59599        \param z Z-coordinate of the pixel value.
59600        \param c C-coordinate of the pixel value.
59601        \param out_value Default value returned if \c offset is outside image bounds.
59602        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
59603     **/
59604     T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
59605       return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
59606     }
59607 
59608     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const.
59609     T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
59610       return (pos<0 || pos>=width())?out_value:(*this)(pos,x,y,z,c);
59611     }
59612 
59613     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos).
59614     /**
59615        \param pos Index of the image element to access.
59616        \param x X-coordinate of the pixel value.
59617        \param y Y-coordinate of the pixel value.
59618        \param z Z-coordinate of the pixel value.
59619        \param c C-coordinate of the pixel value.
59620        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
59621     **/
59622     T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
59623       if (is_empty())
59624         throw CImgInstanceException(_cimglist_instance
59625                                     "atN(): Empty instance.",
59626                                     cimglist_instance);
59627       return _atN(pos,x,y,z,c);
59628     }
59629 
59630     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const.
59631     T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
59632       if (is_empty())
59633         throw CImgInstanceException(_cimglist_instance
59634                                     "atN(): Empty instance.",
59635                                     cimglist_instance);
59636       return _atN(pos,x,y,z,c);
59637     }
59638 
59639     T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
59640       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
59641     }
59642 
59643     T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
59644       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
59645     }
59646 
59647     //@}
59648     //-------------------------------------
59649     //
59650     //! \name Instance Checking
59651     //@{
59652     //-------------------------------------
59653 
59654     //! Return \c true if list is empty.
59655     /**
59656     **/
59657     bool is_empty() const {
59658       return (!_data || !_width);
59659     }
59660 
59661     //! Test if number of image elements is equal to specified value.
59662     /**
59663         \param size_n Number of image elements to test.
59664     **/
59665     bool is_sameN(const unsigned int size_n) const {
59666       return _width==size_n;
59667     }
59668 
59669     //! Test if number of image elements is equal between two images lists.
59670     /**
59671         \param list Input list to compare with.
59672     **/
59673     template<typename t>
59674     bool is_sameN(const CImgList<t>& list) const {
59675       return is_sameN(list._width);
59676     }
59677 
59678     // Define useful functions to check list dimensions.
59679     // (cannot be documented because macro-generated).
59680 #define _cimglist_def_is_same1(axis) \
59681     bool is_same##axis(const unsigned int val) const { \
59682       bool res = true; \
59683       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); \
59684       return res; \
59685     } \
59686     bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
59687       return is_sameN(n) && is_same##axis(val); \
59688     } \
59689 
59690 #define _cimglist_def_is_same2(axis1,axis2) \
59691     bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
59692       bool res = true; \
59693       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); \
59694       return res; \
59695     } \
59696     bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
59697       return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
59698     } \
59699 
59700 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
59701     bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
59702                                       const unsigned int val3) const { \
59703       bool res = true; \
59704       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
59705       return res; \
59706     } \
59707     bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
59708                                        const unsigned int val2, const unsigned int val3) const { \
59709       return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
59710     } \
59711 
59712 #define _cimglist_def_is_same(axis) \
59713     template<typename t> bool is_same##axis(const CImg<t>& img) const { \
59714       bool res = true; \
59715       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); \
59716       return res; \
59717     } \
59718     template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
59719       const unsigned int lmin = std::min(_width,list._width); \
59720       bool res = true; \
59721       for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); \
59722       return res; \
59723     } \
59724     template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
59725       return (is_sameN(n) && is_same##axis(img)); \
59726     } \
59727     template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
59728       return (is_sameN(list) && is_same##axis(list)); \
59729     }
59730 
59731     _cimglist_def_is_same(XY)
59732     _cimglist_def_is_same(XZ)
59733     _cimglist_def_is_same(XC)
59734     _cimglist_def_is_same(YZ)
59735     _cimglist_def_is_same(YC)
59736     _cimglist_def_is_same(XYZ)
59737     _cimglist_def_is_same(XYC)
59738     _cimglist_def_is_same(YZC)
59739     _cimglist_def_is_same(XYZC)
59740     _cimglist_def_is_same1(X)
59741     _cimglist_def_is_same1(Y)
59742     _cimglist_def_is_same1(Z)
59743     _cimglist_def_is_same1(C)
59744     _cimglist_def_is_same2(X,Y)
59745     _cimglist_def_is_same2(X,Z)
59746     _cimglist_def_is_same2(X,C)
59747     _cimglist_def_is_same2(Y,Z)
59748     _cimglist_def_is_same2(Y,C)
59749     _cimglist_def_is_same2(Z,C)
59750     _cimglist_def_is_same3(X,Y,Z)
59751     _cimglist_def_is_same3(X,Y,C)
59752     _cimglist_def_is_same3(X,Z,C)
59753     _cimglist_def_is_same3(Y,Z,C)
59754 
59755     //! Test if dimensions of each image of the list match specified arguments.
59756     /**
59757       \param dx Checked image width.
59758       \param dy Checked image height.
59759       \param dz Checked image depth.
59760       \param dc Checked image spectrum.
59761     **/
59762     bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
59763                      const unsigned int dz, const unsigned int dc) const {
59764       bool res = true;
59765       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
59766       return res;
59767     }
59768 
59769     //! Test if list dimensions match specified arguments.
59770     /**
59771        \param n Number of images in the list.
59772        \param dx Checked image width.
59773        \param dy Checked image height.
59774        \param dz Checked image depth.
59775        \param dc Checked image spectrum.
59776     **/
59777     bool is_sameNXYZC(const unsigned int n,
59778                       const unsigned int dx, const unsigned int dy,
59779                       const unsigned int dz, const unsigned int dc) const {
59780       return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
59781     }
59782 
59783     //! Test if list contains one particular pixel location.
59784     /**
59785        \param n Index of the image whom checked pixel value belong to.
59786        \param x X-coordinate of the checked pixel value.
59787        \param y Y-coordinate of the checked pixel value.
59788        \param z Z-coordinate of the checked pixel value.
59789        \param c C-coordinate of the checked pixel value.
59790     **/
59791     bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
59792       if (is_empty()) return false;
59793       return n>=0 && n<width() && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
59794         z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
59795     }
59796 
59797     //! Test if list contains image with specified index.
59798     /**
59799        \param n Index of the checked image.
59800     **/
59801     bool containsN(const int n) const {
59802       if (is_empty()) return false;
59803       return n>=0 && n<width();
59804     }
59805 
59806     //! Test if one image of the list contains the specified referenced value.
59807     /**
59808        \param pixel Reference to pixel value to test.
59809        \param[out] n Index of image containing the pixel value, if test succeeds.
59810        \param[out] x X-coordinate of the pixel value, if test succeeds.
59811        \param[out] y Y-coordinate of the pixel value, if test succeeds.
59812        \param[out] z Z-coordinate of the pixel value, if test succeeds.
59813        \param[out] c C-coordinate of the pixel value, if test succeeds.
59814        \note If true, set coordinates (n,x,y,z,c).
59815     **/
59816     template<typename t>
59817     bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
59818       if (is_empty()) return false;
59819       cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
59820       return false;
59821     }
59822 
59823     //! Test if one of the image list contains the specified referenced value.
59824     /**
59825        \param pixel Reference to pixel value to test.
59826        \param[out] n Index of image containing the pixel value, if test succeeds.
59827        \param[out] x X-coordinate of the pixel value, if test succeeds.
59828        \param[out] y Y-coordinate of the pixel value, if test succeeds.
59829        \param[out] z Z-coordinate of the pixel value, if test succeeds.
59830        \note If true, set coordinates (n,x,y,z).
59831     **/
59832     template<typename t>
59833     bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
59834       t c;
59835       return contains(pixel,n,x,y,z,c);
59836     }
59837 
59838     //! Test if one of the image list contains the specified referenced value.
59839     /**
59840        \param pixel Reference to pixel value to test.
59841        \param[out] n Index of image containing the pixel value, if test succeeds.
59842        \param[out] x X-coordinate of the pixel value, if test succeeds.
59843        \param[out] y Y-coordinate of the pixel value, if test succeeds.
59844        \note If true, set coordinates (n,x,y).
59845     **/
59846     template<typename t>
59847     bool contains(const T& pixel, t& n, t& x, t&y) const {
59848       t z, c;
59849       return contains(pixel,n,x,y,z,c);
59850     }
59851 
59852     //! Test if one of the image list contains the specified referenced value.
59853     /**
59854        \param pixel Reference to pixel value to test.
59855        \param[out] n Index of image containing the pixel value, if test succeeds.
59856        \param[out] x X-coordinate of the pixel value, if test succeeds.
59857        \note If true, set coordinates (n,x).
59858     **/
59859     template<typename t>
59860     bool contains(const T& pixel, t& n, t& x) const {
59861       t y, z, c;
59862       return contains(pixel,n,x,y,z,c);
59863     }
59864 
59865     //! Test if one of the image list contains the specified referenced value.
59866     /**
59867        \param pixel Reference to pixel value to test.
59868        \param[out] n Index of image containing the pixel value, if test succeeds.
59869        \note If true, set coordinates (n).
59870     **/
59871     template<typename t>
59872     bool contains(const T& pixel, t& n) const {
59873       t x, y, z, c;
59874       return contains(pixel,n,x,y,z,c);
59875     }
59876 
59877     //! Test if one of the image list contains the specified referenced value.
59878     /**
59879        \param pixel Reference to pixel value to test.
59880     **/
59881     bool contains(const T& pixel) const {
59882       unsigned int n, x, y, z, c;
59883       return contains(pixel,n,x,y,z,c);
59884     }
59885 
59886     //! Test if the list contains the image 'img'.
59887     /**
59888        \param img Reference to image to test.
59889        \param[out] n Index of image in the list, if test succeeds.
59890        \note If true, returns the position (n) of the image in the list.
59891     **/
59892     template<typename t>
59893     bool contains(const CImg<T>& img, t& n) const {
59894       if (is_empty()) return false;
59895       const CImg<T> *const ptr = &img;
59896       cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; }
59897       return false;
59898     }
59899 
59900     //! Test if the list contains the image img.
59901     /**
59902        \param img Reference to image to test.
59903     **/
59904     bool contains(const CImg<T>& img) const {
59905       unsigned int n;
59906       return contains(img,n);
59907     }
59908 
59909     //@}
59910     //-------------------------------------
59911     //
59912     //! \name Mathematical Functions
59913     //@{
59914     //-------------------------------------
59915 
59916     //! Return a reference to the minimum pixel value of the instance list.
59917     /**
59918     **/
59919     T& min() {
59920       bool is_all_empty = true;
59921       T *ptr_min = 0;
59922       cimglist_for(*this,l) if (!_data[l].is_empty()) {
59923         ptr_min = _data[l]._data;
59924         is_all_empty = false;
59925         break;
59926       }
59927       if (is_all_empty)
59928         throw CImgInstanceException(_cimglist_instance
59929                                     "min(): %s.",
59930                                     _data?"List of empty images":"Empty instance",
59931                                     cimglist_instance);
59932       T min_value = *ptr_min;
59933       cimglist_for(*this,l) {
59934         const CImg<T>& img = _data[l];
59935         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
59936       }
59937       return *ptr_min;
59938     }
59939 
59940     //! Return a reference to the minimum pixel value of the instance list \const.
59941     const T& min() const {
59942       bool is_all_empty = true;
59943       T *ptr_min = 0;
59944       cimglist_for(*this,l) if (!_data[l].is_empty()) {
59945         ptr_min = _data[l]._data;
59946         is_all_empty = false;
59947         break;
59948       }
59949       if (is_all_empty)
59950         throw CImgInstanceException(_cimglist_instance
59951                                     "min(): %s.",
59952                                     _data?"List of empty images":"Empty instance",
59953                                     cimglist_instance);
59954       T min_value = *ptr_min;
59955       cimglist_for(*this,l) {
59956         const CImg<T>& img = _data[l];
59957         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
59958       }
59959       return *ptr_min;
59960     }
59961 
59962     //! Return a reference to the maximum pixel value of the instance list.
59963     /**
59964     **/
59965     T& max() {
59966       bool is_all_empty = true;
59967       T *ptr_max = 0;
59968       cimglist_for(*this,l) if (!_data[l].is_empty()) {
59969         ptr_max = _data[l]._data;
59970         is_all_empty = false;
59971         break;
59972       }
59973       if (is_all_empty)
59974         throw CImgInstanceException(_cimglist_instance
59975                                     "max(): %s.",
59976                                     _data?"List of empty images":"Empty instance",
59977                                     cimglist_instance);
59978       T max_value = *ptr_max;
59979       cimglist_for(*this,l) {
59980         const CImg<T>& img = _data[l];
59981         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
59982       }
59983       return *ptr_max;
59984     }
59985 
59986     //! Return a reference to the maximum pixel value of the instance list \const.
59987     const T& max() const {
59988       bool is_all_empty = true;
59989       T *ptr_max = 0;
59990       cimglist_for(*this,l) if (!_data[l].is_empty()) {
59991         ptr_max = _data[l]._data;
59992         is_all_empty = false;
59993         break;
59994       }
59995       if (is_all_empty)
59996         throw CImgInstanceException(_cimglist_instance
59997                                     "max(): %s.",
59998                                     _data?"List of empty images":"Empty instance",
59999                                     cimglist_instance);
60000       T max_value = *ptr_max;
60001       cimglist_for(*this,l) {
60002         const CImg<T>& img = _data[l];
60003         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
60004       }
60005       return *ptr_max;
60006     }
60007 
60008     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
60009     /**
60010        \param[out] max_val Value of the maximum value found.
60011     **/
60012     template<typename t>
60013     T& min_max(t& max_val) {
60014       bool is_all_empty = true;
60015       T *ptr_min = 0;
60016       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60017         ptr_min = _data[l]._data;
60018         is_all_empty = false;
60019         break;
60020       }
60021       if (is_all_empty)
60022         throw CImgInstanceException(_cimglist_instance
60023                                     "min_max(): %s.",
60024                                     _data?"List of empty images":"Empty instance",
60025                                     cimglist_instance);
60026       T min_value = *ptr_min, max_value = min_value;
60027       cimglist_for(*this,l) {
60028         const CImg<T>& img = _data[l];
60029         cimg_for(img,ptrs,T) {
60030           const T val = *ptrs;
60031           if (val<min_value) { min_value = val; ptr_min = ptrs; }
60032           if (val>max_value) max_value = val;
60033         }
60034       }
60035       max_val = (t)max_value;
60036       return *ptr_min;
60037     }
60038 
60039     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
60040     /**
60041        \param[out] max_val Value of the maximum value found.
60042     **/
60043     template<typename t>
60044     const T& min_max(t& max_val) const {
60045       bool is_all_empty = true;
60046       T *ptr_min = 0;
60047       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60048         ptr_min = _data[l]._data;
60049         is_all_empty = false;
60050         break;
60051       }
60052       if (is_all_empty)
60053         throw CImgInstanceException(_cimglist_instance
60054                                     "min_max(): %s.",
60055                                     _data?"List of empty images":"Empty instance",
60056                                     cimglist_instance);
60057       T min_value = *ptr_min, max_value = min_value;
60058       cimglist_for(*this,l) {
60059         const CImg<T>& img = _data[l];
60060         cimg_for(img,ptrs,T) {
60061           const T val = *ptrs;
60062           if (val<min_value) { min_value = val; ptr_min = ptrs; }
60063           if (val>max_value) max_value = val;
60064         }
60065       }
60066       max_val = (t)max_value;
60067       return *ptr_min;
60068     }
60069 
60070     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
60071     /**
60072        \param[out] min_val Value of the minimum value found.
60073     **/
60074     template<typename t>
60075     T& max_min(t& min_val) {
60076       bool is_all_empty = true;
60077       T *ptr_max = 0;
60078       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60079         ptr_max = _data[l]._data;
60080         is_all_empty = false;
60081         break;
60082       }
60083       if (is_all_empty)
60084         throw CImgInstanceException(_cimglist_instance
60085                                     "max_min(): %s.",
60086                                     _data?"List of empty images":"Empty instance",
60087                                     cimglist_instance);
60088       T min_value = *ptr_max, max_value = min_value;
60089       cimglist_for(*this,l) {
60090         const CImg<T>& img = _data[l];
60091         cimg_for(img,ptrs,T) {
60092           const T val = *ptrs;
60093           if (val>max_value) { max_value = val; ptr_max = ptrs; }
60094           if (val<min_value) min_value = val;
60095         }
60096       }
60097       min_val = (t)min_value;
60098       return *ptr_max;
60099     }
60100 
60101     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
60102     template<typename t>
60103     const T& max_min(t& min_val) const {
60104       bool is_all_empty = true;
60105       T *ptr_max = 0;
60106       cimglist_for(*this,l) if (!_data[l].is_empty()) {
60107         ptr_max = _data[l]._data;
60108         is_all_empty = false;
60109         break;
60110       }
60111       if (is_all_empty)
60112         throw CImgInstanceException(_cimglist_instance
60113                                     "max_min(): %s.",
60114                                     _data?"List of empty images":"Empty instance",
60115                                     cimglist_instance);
60116       T min_value = *ptr_max, max_value = min_value;
60117       cimglist_for(*this,l) {
60118         const CImg<T>& img = _data[l];
60119         cimg_for(img,ptrs,T) {
60120           const T val = *ptrs;
60121           if (val>max_value) { max_value = val; ptr_max = ptrs; }
60122           if (val<min_value) min_value = val;
60123         }
60124       }
60125       min_val = (t)min_value;
60126       return *ptr_max;
60127     }
60128 
60129     //@}
60130     //---------------------------
60131     //
60132     //! \name List Manipulation
60133     //@{
60134     //---------------------------
60135 
60136     //! Insert a copy of the image \c img into the current image list, at position \c pos.
60137     /**
60138         \param img Image to insert a copy to the list.
60139         \param pos Index of the insertion.
60140         \param is_shared Tells if the inserted image is a shared copy of \c img or not.
60141     **/
60142     template<typename t>
60143     CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
60144       const unsigned int npos = pos==~0U?_width:pos;
60145       if (npos>_width)
60146         throw CImgArgumentException(_cimglist_instance
60147                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
60148                                     "at position %u.",
60149                                     cimglist_instance,
60150                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
60151       if (is_shared)
60152         throw CImgArgumentException(_cimglist_instance
60153                                     "insert(): Invalid insertion request of specified shared image "
60154                                     "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
60155                                     cimglist_instance,
60156                                     img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
60157 
60158       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
60159                                                                         (_allocated_width=16)]:0;
60160       if (!_data) { // Insert new element into empty list
60161         _data = new_data;
60162         *_data = img;
60163       } else {
60164         if (new_data) { // Insert with re-allocation
60165           if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
60166           if (npos!=_width - 1)
60167             std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
60168           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
60169           delete[] _data;
60170           _data = new_data;
60171         } else if (npos!=_width - 1) // Insert without re-allocation
60172           std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
60173         _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
60174         _data[npos]._data = 0;
60175         _data[npos] = img;
60176       }
60177       return *this;
60178     }
60179 
60180     //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
60181     CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
60182       const unsigned int npos = pos==~0U?_width:pos;
60183       if (npos>_width)
60184         throw CImgArgumentException(_cimglist_instance
60185                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
60186                                     "at position %u.",
60187                                     cimglist_instance,
60188                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
60189       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
60190                                                                         (_allocated_width=16)]:0;
60191       if (!_data) { // Insert new element into empty list
60192         _data = new_data;
60193         if (is_shared && img) {
60194           _data->_width = img._width;
60195           _data->_height = img._height;
60196           _data->_depth = img._depth;
60197           _data->_spectrum = img._spectrum;
60198           _data->_is_shared = true;
60199           _data->_data = img._data;
60200         } else *_data = img;
60201       }
60202       else {
60203         if (new_data) { // Insert with re-allocation
60204           if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
60205           if (npos!=_width - 1)
60206             std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
60207           if (is_shared && img) {
60208             new_data[npos]._width = img._width;
60209             new_data[npos]._height = img._height;
60210             new_data[npos]._depth = img._depth;
60211             new_data[npos]._spectrum = img._spectrum;
60212             new_data[npos]._is_shared = true;
60213             new_data[npos]._data = img._data;
60214           } else {
60215             new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
60216             new_data[npos]._data = 0;
60217             new_data[npos] = img;
60218           }
60219           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
60220           delete[] _data;
60221           _data = new_data;
60222         } else { // Insert without re-allocation
60223           if (npos!=_width - 1)
60224             std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
60225           if (is_shared && img) {
60226             _data[npos]._width = img._width;
60227             _data[npos]._height = img._height;
60228             _data[npos]._depth = img._depth;
60229             _data[npos]._spectrum = img._spectrum;
60230             _data[npos]._is_shared = true;
60231             _data[npos]._data = img._data;
60232           } else {
60233             _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
60234             _data[npos]._data = 0;
60235             _data[npos] = img;
60236           }
60237         }
60238       }
60239       return *this;
60240     }
60241 
60242     //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
60243     template<typename t>
60244     CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
60245       return (+*this).insert(img,pos,is_shared);
60246     }
60247 
60248     //! Insert n empty images img into the current image list, at position \p pos.
60249     /**
60250        \param n Number of empty images to insert.
60251        \param pos Index of the insertion.
60252     **/
60253     CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
60254       CImg<T> empty;
60255       if (!n) return *this;
60256       const unsigned int npos = pos==~0U?_width:pos;
60257       for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
60258       return *this;
60259     }
60260 
60261     //! Insert n empty images img into the current image list, at position \p pos \newinstance.
60262     CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
60263       return (+*this).insert(n,pos);
60264     }
60265 
60266     //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
60267     /**
60268        \param n Number of image copies to insert.
60269        \param img Image to insert by copy.
60270        \param pos Index of the insertion.
60271        \param is_shared Tells if inserted images are shared copies of \c img or not.
60272     **/
60273     template<typename t>
60274     CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
60275                         const bool is_shared=false) {
60276       if (!n) return *this;
60277       const unsigned int npos = pos==~0U?_width:pos;
60278       insert(img,npos,is_shared);
60279       for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared);
60280       return *this;
60281     }
60282 
60283     //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
60284     template<typename t>
60285     CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
60286                            const bool is_shared=false) const {
60287       return (+*this).insert(n,img,pos,is_shared);
60288     }
60289 
60290     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
60291     /**
60292       \param list Image list to insert.
60293       \param pos Index of the insertion.
60294       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
60295     **/
60296     template<typename t>
60297     CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
60298       const unsigned int npos = pos==~0U?_width:pos;
60299       if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared);
60300       else insert(CImgList<T>(list),npos,is_shared);
60301       return *this;
60302     }
60303 
60304     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
60305     template<typename t>
60306     CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
60307       return (+*this).insert(list,pos,is_shared);
60308     }
60309 
60310     //! Insert n copies of the list \c list at position \c pos of the current list.
60311     /**
60312       \param n Number of list copies to insert.
60313       \param list Image list to insert.
60314       \param pos Index of the insertion.
60315       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
60316     **/
60317     template<typename t>
60318     CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
60319                         const bool is_shared=false) {
60320       if (!n) return *this;
60321       const unsigned int npos = pos==~0U?_width:pos;
60322       for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
60323       return *this;
60324     }
60325 
60326     //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
60327     template<typename t>
60328     CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
60329                            const bool is_shared=false) const {
60330       return (+*this).insert(n,list,pos,is_shared);
60331     }
60332 
60333     //! Remove all images between from indexes.
60334     /**
60335       \param pos1 Starting index of the removal.
60336       \param pos2 Ending index of the removal.
60337     **/
60338     CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
60339       const unsigned int
60340         npos1 = pos1<pos2?pos1:pos2,
60341         tpos2 = pos1<pos2?pos2:pos1,
60342         npos2 = tpos2<_width?tpos2:_width - 1;
60343       if (npos1>=_width)
60344         throw CImgArgumentException(_cimglist_instance
60345                                     "remove(): Invalid remove request at positions %u->%u.",
60346                                     cimglist_instance,
60347                                     npos1,tpos2);
60348       else {
60349         if (tpos2>=_width)
60350           throw CImgArgumentException(_cimglist_instance
60351                                       "remove(): Invalid remove request at positions %u->%u.",
60352                                       cimglist_instance,
60353                                       npos1,tpos2);
60354 
60355         for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
60356         const unsigned int nb = 1 + npos2 - npos1;
60357         if (!(_width-=nb)) return assign();
60358         if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation
60359           if (npos1!=_width)
60360             std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
60361           std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb);
60362         } else { // Removing items with reallocation
60363           _allocated_width>>=4;
60364           while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
60365           CImg<T> *const new_data = new CImg<T>[_allocated_width];
60366           if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos1);
60367           if (npos1!=_width)
60368             std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
60369           if (_width!=_allocated_width)
60370             std::memset((void*)(new_data + _width),0,sizeof(CImg<T>)*(_allocated_width - _width));
60371           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width + nb));
60372           delete[] _data;
60373           _data = new_data;
60374         }
60375       }
60376       return *this;
60377     }
60378 
60379     //! Remove all images between from indexes \newinstance.
60380     CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
60381       return (+*this).remove(pos1,pos2);
60382     }
60383 
60384     //! Remove image at index \c pos from the image list.
60385     /**
60386       \param pos Index of the image to remove.
60387     **/
60388     CImgList<T>& remove(const unsigned int pos) {
60389       return remove(pos,pos);
60390     }
60391 
60392     //! Remove image at index \c pos from the image list \newinstance.
60393     CImgList<T> get_remove(const unsigned int pos) const {
60394       return (+*this).remove(pos);
60395     }
60396 
60397     //! Remove last image.
60398     /**
60399     **/
60400     CImgList<T>& remove() {
60401       return remove(_width - 1);
60402     }
60403 
60404     //! Remove last image \newinstance.
60405     CImgList<T> get_remove() const {
60406       return (+*this).remove();
60407     }
60408 
60409     //! Reverse list order.
60410     CImgList<T>& reverse() {
60411       for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]);
60412       return *this;
60413     }
60414 
60415     //! Reverse list order \newinstance.
60416     CImgList<T> get_reverse() const {
60417       return (+*this).reverse();
60418     }
60419 
60420     //! Return a sublist.
60421     /**
60422       \param pos0 Starting index of the sublist.
60423       \param pos1 Ending index of the sublist.
60424     **/
60425     CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
60426       return get_images(pos0,pos1).move_to(*this);
60427     }
60428 
60429     //! Return a sublist \newinstance.
60430     CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
60431       if (pos0>pos1 || pos1>=_width)
60432         throw CImgArgumentException(_cimglist_instance
60433                                     "images(): Specified sub-list indices (%u->%u) are out of bounds.",
60434                                     cimglist_instance,
60435                                     pos0,pos1);
60436       CImgList<T> res(pos1 - pos0 + 1);
60437       cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
60438       return res;
60439     }
60440 
60441     //! Return a shared sublist.
60442     /**
60443       \param pos0 Starting index of the sublist.
60444       \param pos1 Ending index of the sublist.
60445     **/
60446     CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
60447       if (pos0>pos1 || pos1>=_width)
60448         throw CImgArgumentException(_cimglist_instance
60449                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
60450                                     cimglist_instance,
60451                                     pos0,pos1);
60452       CImgList<T> res(pos1 - pos0 + 1);
60453       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
60454       return res;
60455     }
60456 
60457     //! Return a shared sublist \newinstance.
60458     const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
60459       if (pos0>pos1 || pos1>=_width)
60460         throw CImgArgumentException(_cimglist_instance
60461                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
60462                                     cimglist_instance,
60463                                     pos0,pos1);
60464       CImgList<T> res(pos1 - pos0 + 1);
60465       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
60466       return res;
60467     }
60468 
60469     //! Return a single image which is the appending of all images of the current CImgList instance.
60470     /**
60471        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
60472        \param align Appending alignment.
60473     **/
60474     CImg<T> get_append(const char axis, const float align=0) const {
60475       if (is_empty()) return CImg<T>();
60476       if (_width==1) return +((*this)[0]);
60477       unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
60478       CImg<T> res;
60479       switch (cimg::lowercase(axis)) {
60480       case 'x' : { // Along the X-axis
60481         cimglist_for(*this,l) {
60482           const CImg<T>& img = (*this)[l];
60483           if (img) {
60484             dx+=img._width;
60485             dy = std::max(dy,img._height);
60486             dz = std::max(dz,img._depth);
60487             dc = std::max(dc,img._spectrum);
60488           }
60489         }
60490         res.assign(dx,dy,dz,dc,(T)0);
60491         if (res) cimglist_for(*this,l) {
60492             const CImg<T>& img = (*this)[l];
60493             if (img) {
60494               if (img._height==1 && img._depth==1 && img._spectrum==1 &&
60495                   res._height==1 && res._depth==1 && res._spectrum==1)
60496                 std::memcpy(&res[pos],img._data,sizeof(T)*img._width);
60497               else
60498                 res.draw_image(pos,
60499                                (int)(align*(dy - img._height)),
60500                                (int)(align*(dz - img._depth)),
60501                                (int)(align*(dc - img._spectrum)),
60502                                img);
60503             }
60504             pos+=img._width;
60505           }
60506       } break;
60507       case 'y' : { // Along the Y-axis
60508         cimglist_for(*this,l) {
60509           const CImg<T>& img = (*this)[l];
60510           if (img) {
60511             dx = std::max(dx,img._width);
60512             dy+=img._height;
60513             dz = std::max(dz,img._depth);
60514             dc = std::max(dc,img._spectrum);
60515           }
60516         }
60517         res.assign(dx,dy,dz,dc,(T)0);
60518         if (res) cimglist_for(*this,l) {
60519             const CImg<T>& img = (*this)[l];
60520             if (img) {
60521               if (img._width==1 && img._depth==1 && img._spectrum==1 &&
60522                   res._width==1 && res._depth==1 && res._spectrum==1)
60523                 std::memcpy(&res[pos],img._data,sizeof(T)*img._height);
60524               else
60525                 res.draw_image((int)(align*(dx - img._width)),
60526                                pos,
60527                                (int)(align*(dz - img._depth)),
60528                                (int)(align*(dc - img._spectrum)),
60529                                img);
60530             }
60531             pos+=img._height;
60532           }
60533       } break;
60534       case 'z' : { // Along the Z-axis
60535         cimglist_for(*this,l) {
60536           const CImg<T>& img = (*this)[l];
60537           if (img) {
60538             dx = std::max(dx,img._width);
60539             dy = std::max(dy,img._height);
60540             dz+=img._depth;
60541             dc = std::max(dc,img._spectrum);
60542           }
60543         }
60544         res.assign(dx,dy,dz,dc,(T)0);
60545         if (res) cimglist_for(*this,l) {
60546             const CImg<T>& img = (*this)[l];
60547             if (img) {
60548               if (img._width==1 && img._height==1 && img._spectrum==1 &&
60549                   res._width==1 && res._height==1 && res._spectrum==1)
60550                 std::memcpy(&res[pos],img._data,sizeof(T)*img._depth);
60551               else
60552                 res.draw_image((int)(align*(dx - img._width)),
60553                                (int)(align*(dy - img._height)),
60554                                pos,
60555                                (int)(align*(dc - img._spectrum)),
60556                                img);
60557             }
60558             pos+=img._depth;
60559           }
60560       } break;
60561       default : { // Along the C-axis
60562         cimglist_for(*this,l) {
60563           const CImg<T>& img = (*this)[l];
60564           if (img) {
60565             dx = std::max(dx,img._width);
60566             dy = std::max(dy,img._height);
60567             dz = std::max(dz,img._depth);
60568             dc+=img._spectrum;
60569           }
60570         }
60571         res.assign(dx,dy,dz,dc,(T)0);
60572         if (res) cimglist_for(*this,l) {
60573             const CImg<T>& img = (*this)[l];
60574             if (img) {
60575               if (img._width==1 && img._height==1 && img._depth==1 &&
60576                   res._width==1 && res._height==1 && res._depth==1)
60577                 std::memcpy(&res[pos],img._data,sizeof(T)*img._spectrum);
60578               else
60579                 res.draw_image((int)(align*(dx - img._width)),
60580                                (int)(align*(dy - img._height)),
60581                                (int)(align*(dz - img._depth)),
60582                                pos,
60583                                img);
60584             }
60585             pos+=img._spectrum;
60586           }
60587       }
60588       }
60589       return res;
60590     }
60591 
60592     //! Return a list where each image has been split along the specified axis.
60593     /**
60594         \param axis Axis to split images along.
60595         \param nb Number of split parts for each image.
60596     **/
60597     CImgList<T>& split(const char axis, const int nb=-1) {
60598       return get_split(axis,nb).move_to(*this);
60599     }
60600 
60601     //! Return a list where each image has been split along the specified axis \newinstance.
60602     CImgList<T> get_split(const char axis, const int nb=-1) const {
60603       CImgList<T> res;
60604       cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
60605       return res;
60606     }
60607 
60608     //! Insert image at the end of the list.
60609     /**
60610       \param img Image to insert.
60611     **/
60612     template<typename t>
60613     CImgList<T>& push_back(const CImg<t>& img) {
60614       return insert(img);
60615     }
60616 
60617     //! Insert image at the front of the list.
60618     /**
60619       \param img Image to insert.
60620     **/
60621     template<typename t>
60622     CImgList<T>& push_front(const CImg<t>& img) {
60623       return insert(img,0);
60624     }
60625 
60626     //! Insert list at the end of the current list.
60627     /**
60628       \param list List to insert.
60629     **/
60630     template<typename t>
60631     CImgList<T>& push_back(const CImgList<t>& list) {
60632       return insert(list);
60633     }
60634 
60635     //! Insert list at the front of the current list.
60636     /**
60637       \param list List to insert.
60638     **/
60639     template<typename t>
60640     CImgList<T>& push_front(const CImgList<t>& list) {
60641       return insert(list,0);
60642     }
60643 
60644     //! Remove last image.
60645     /**
60646     **/
60647     CImgList<T>& pop_back() {
60648       return remove(_width - 1);
60649     }
60650 
60651     //! Remove first image.
60652     /**
60653     **/
60654     CImgList<T>& pop_front() {
60655       return remove(0);
60656     }
60657 
60658     //! Remove image pointed by iterator.
60659     /**
60660       \param iter Iterator pointing to the image to remove.
60661     **/
60662     CImgList<T>& erase(const iterator iter) {
60663       return remove(iter - _data);
60664     }
60665 
60666     //@}
60667     //----------------------------------
60668     //
60669     //! \name Data Input
60670     //@{
60671     //----------------------------------
60672 
60673     //! Display a simple interactive interface to select images or sublists.
60674     /**
60675        \param disp Window instance to display selection and user interface.
60676        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
60677        \param axis Axis along whom images are appended for visualization.
60678        \param align Alignment setting when images have not all the same size.
60679        \param exit_on_anykey Exit function when any key is pressed.
60680        \return A one-column vector containing the selected image indexes.
60681     **/
60682     CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
60683                           const char axis='x', const float align=0,
60684                           const bool exit_on_anykey=false) const {
60685       return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
60686     }
60687 
60688     //! Display a simple interactive interface to select images or sublists.
60689     /**
60690        \param title Title of a new window used to display selection and user interface.
60691        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
60692        \param axis Axis along whom images are appended for visualization.
60693        \param align Alignment setting when images have not all the same size.
60694        \param exit_on_anykey Exit function when any key is pressed.
60695        \return A one-column vector containing the selected image indexes.
60696     **/
60697     CImg<intT> get_select(const char *const title, const bool feature_type=true,
60698                           const char axis='x', const float align=0,
60699                           const bool exit_on_anykey=false) const {
60700       CImgDisplay disp;
60701       return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
60702     }
60703 
60704     CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
60705                        const char axis, const float align, const bool exit_on_anykey,
60706                        const unsigned int orig, const bool resize_disp,
60707                        const bool exit_on_rightbutton, const bool exit_on_wheel) const {
60708       if (is_empty())
60709         throw CImgInstanceException(_cimglist_instance
60710                                     "select(): Empty instance.",
60711                                     cimglist_instance);
60712 
60713       // Create image correspondence table and get list dimensions for visualization.
60714       CImgList<uintT> _indices;
60715       unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
60716       cimglist_for(*this,l) {
60717         const CImg<T>& img = _data[l];
60718         const unsigned int
60719           w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
60720           h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
60721         if (w>max_width) max_width = w;
60722         if (h>max_height) max_height = h;
60723         sum_width+=w; sum_height+=h;
60724         if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
60725         else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
60726       }
60727       const CImg<uintT> indices0 = _indices>'x';
60728 
60729       // Create display window.
60730       if (!disp) {
60731         if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
60732         else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
60733         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
60734       } else {
60735         if (title) disp.set_title("%s",title);
60736         disp.move_inside_screen();
60737       }
60738       if (resize_disp) {
60739         if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
60740         else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
60741       }
60742 
60743       const unsigned int old_normalization = disp.normalization();
60744       bool old_is_resized = disp.is_resized();
60745       disp._normalization = 0;
60746       disp.show().set_key(0).show_mouse();
60747       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
60748 
60749       // Enter event loop.
60750       CImg<ucharT> visu0, visu;
60751       CImg<uintT> indices;
60752       CImg<intT> positions(_width,4,1,1,-1);
60753       int oindex0 = -1, oindex1 = -1, index0 = -1, index1 = -1;
60754       bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
60755       unsigned int key = 0, font_size = 32;
60756 
60757       while (!is_selected && !disp.is_closed() && !key) {
60758 
60759         // Create background image.
60760         if (!visu0) {
60761           visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
60762           (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
60763           unsigned int _ind = 0;
60764           const CImg<T> onexone(1,1,1,1,(T)0);
60765           if (axis=='x')
60766             cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
60767             cimglist_for(*this,ind) {
60768               unsigned int x0 = 0;
60769               while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {}
60770               unsigned int x1 = x0;
60771               while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {}
60772               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
60773               CImg<ucharT> res;
60774               src._get_select(disp,old_normalization,src._width/2,src._height/2,src._depth/2).
60775                 move_to(res);
60776               const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
60777               res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
60778               positions(ind,0) = positions(ind,2) = (int)x0;
60779               positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
60780               positions(ind,2)+=res._width;
60781               positions(ind,3)+=res._height - 1;
60782               visu0.draw_image(positions(ind,0),positions(ind,1),res);
60783             }
60784           else
60785             cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
60786             cimglist_for(*this,ind) {
60787               unsigned int y0 = 0;
60788               while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {}
60789               unsigned int y1 = y0;
60790               while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {}
60791               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
60792               CImg<ucharT> res;
60793               src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
60794                 move_to(res);
60795               const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
60796               res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100);
60797               positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
60798               positions(ind,1) = positions(ind,3) = (int)y0;
60799               positions(ind,2)+=res._width - 1;
60800               positions(ind,3)+=res._height;
60801               visu0.draw_image(positions(ind,0),positions(ind,1),res);
60802             }
60803           if (axis=='x') --positions(_ind,2); else --positions(_ind,3);
60804           update_display = true;
60805         }
60806 
60807         if (!visu || oindex0!=index0 || oindex1!=index1) {
60808           if (index0>=0 && index1>=0) {
60809             visu.assign(visu0,false);
60810             const int indm = std::min(index0,index1), indM = std::max(index0,index1);
60811             for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
60812                 visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
60813                                     background_color,0.2f);
60814                 if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
60815                     (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
60816                   visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
60817                                       foreground_color,0.9f,0xAAAAAAAA);
60818               }
60819             if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u ",font_size,(int)text_down,
60820                                              orig + indm,orig + indM,indM - indm + 1);
60821             else visu.__draw_text(" Image #%u (%u,%u,%u,%u) ",font_size,(int)text_down,
60822                                   orig + index0,
60823                                   _data[index0]._width,
60824                                   _data[index0]._height,
60825                                   _data[index0]._depth,
60826                                   _data[index0]._spectrum);
60827             update_display = true;
60828           } else visu.assign();
60829         }
60830         if (!visu) { visu.assign(visu0,true); update_display = true; }
60831         if (update_display) { visu.display(disp); update_display = false; }
60832         disp.wait();
60833 
60834         // Manage user events.
60835         const int xm = disp.mouse_x(), ym = disp.mouse_y();
60836         int index = -1;
60837 
60838         if (xm>=0) {
60839           index = (int)indices(axis=='x'?xm:ym);
60840           if (disp.button()&1) {
60841             if (!is_clicked) { is_clicked = true; oindex0 = index0; index0 = index; }
60842             oindex1 = index1; index1 = index;
60843             if (!feature_type) is_selected = true;
60844           } else {
60845             if (!is_clicked) { oindex0 = oindex1 = index0; index0 = index1 = index; }
60846             else is_selected = true;
60847           }
60848         } else {
60849           if (is_clicked) {
60850             if (!(disp.button()&1)) { is_clicked = is_selected = false; index0 = index1 = -1; }
60851             else index1 = -1;
60852           } else index0 = index1 = -1;
60853         }
60854 
60855         if (disp.button()&4) { is_clicked = is_selected = false; index0 = index1 = -1; }
60856         if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; index1 = index0 = -1; }
60857         if (disp.wheel() && exit_on_wheel) is_selected = true;
60858 
60859         CImg<charT> filename(32);
60860         switch (key = disp.key()) {
60861 #if cimg_OS!=2
60862         case cimg::keyCTRLRIGHT :
60863 #endif
60864         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
60865         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
60866             disp.set_fullscreen(false).
60867               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
60868                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
60869               _is_resized = true;
60870             disp.set_key(key,false); key = 0; visu0.assign();
60871           } break;
60872         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
60873             disp.set_fullscreen(false).
60874               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
60875             disp.set_key(key,false); key = 0; visu0.assign();
60876           } break;
60877         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
60878             disp.set_fullscreen(false).
60879               resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
60880               _is_resized = true;
60881             disp.set_key(key,false); key = 0; visu0.assign();
60882           } break;
60883         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
60884             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
60885             disp.set_key(key,false); key = 0; visu0.assign();
60886           } break;
60887         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
60888             static unsigned int snap_number = 0;
60889             std::FILE *file;
60890             do {
60891               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
60892               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
60893             } while (file);
60894             if (visu0) {
60895               (+visu0).__draw_text(" Saving snapshot... ",font_size,(int)text_down).display(disp);
60896               visu0.save(filename);
60897               (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
60898             }
60899             disp.set_key(key,false).wait(); key = 0;
60900           } break;
60901         case cimg::keyO :
60902           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
60903             static unsigned int snap_number = 0;
60904             std::FILE *file;
60905             do {
60906 #ifdef cimg_use_zlib
60907               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
60908 #else
60909               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
60910 #endif
60911               if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
60912             } while (file);
60913             (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
60914             save(filename);
60915             (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
60916             disp.set_key(key,false).wait(); key = 0;
60917           } break;
60918         }
60919         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
60920         if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
60921         else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
60922         if (!exit_on_anykey && key && key!=cimg::keyESC &&
60923             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
60924           key = 0;
60925         }
60926       }
60927       CImg<intT> res(1,2,1,1,-1);
60928       if (is_selected) {
60929         if (feature_type) res.fill(std::min(index0,index1),std::max(index0,index1));
60930         else res.fill(index0);
60931       }
60932       if (!(disp.button()&2)) disp.set_button();
60933       disp._normalization = old_normalization;
60934       disp._is_resized = old_is_resized;
60935       disp.set_key(key);
60936       return res;
60937     }
60938 
60939     //! Load a list from a file.
60940     /**
60941      \param filename Filename to read data from.
60942     **/
60943     CImgList<T>& load(const char *const filename) {
60944       if (!filename)
60945         throw CImgArgumentException(_cimglist_instance
60946                                     "load(): Specified filename is (null).",
60947                                     cimglist_instance);
60948 
60949       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
60950         CImg<charT> filename_local(256);
60951         load(cimg::load_network(filename,filename_local));
60952         std::remove(filename_local);
60953         return *this;
60954       }
60955 
60956       const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
60957       const char *const ext = cimg::split_filename(filename);
60958       const unsigned int omode = cimg::exception_mode();
60959       cimg::exception_mode(0);
60960       bool is_loaded = true;
60961       try {
60962 #ifdef cimglist_load_plugin
60963         cimglist_load_plugin(filename);
60964 #endif
60965 #ifdef cimglist_load_plugin1
60966         cimglist_load_plugin1(filename);
60967 #endif
60968 #ifdef cimglist_load_plugin2
60969         cimglist_load_plugin2(filename);
60970 #endif
60971 #ifdef cimglist_load_plugin3
60972         cimglist_load_plugin3(filename);
60973 #endif
60974 #ifdef cimglist_load_plugin4
60975         cimglist_load_plugin4(filename);
60976 #endif
60977 #ifdef cimglist_load_plugin5
60978         cimglist_load_plugin5(filename);
60979 #endif
60980 #ifdef cimglist_load_plugin6
60981         cimglist_load_plugin6(filename);
60982 #endif
60983 #ifdef cimglist_load_plugin7
60984         cimglist_load_plugin7(filename);
60985 #endif
60986 #ifdef cimglist_load_plugin8
60987         cimglist_load_plugin8(filename);
60988 #endif
60989         if (!cimg::strcasecmp(ext,"tif") ||
60990             !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
60991         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
60992         else if (!cimg::strcasecmp(ext,"cimg") ||
60993                  !cimg::strcasecmp(ext,"cimgz") ||
60994                  !*ext) load_cimg(filename);
60995         else if (!cimg::strcasecmp(ext,"rec") ||
60996                  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
60997         else if (!cimg::strcasecmp(ext,"avi") ||
60998                  !cimg::strcasecmp(ext,"mov") ||
60999                  !cimg::strcasecmp(ext,"asf") ||
61000                  !cimg::strcasecmp(ext,"divx") ||
61001                  !cimg::strcasecmp(ext,"flv") ||
61002                  !cimg::strcasecmp(ext,"mpg") ||
61003                  !cimg::strcasecmp(ext,"m1v") ||
61004                  !cimg::strcasecmp(ext,"m2v") ||
61005                  !cimg::strcasecmp(ext,"m4v") ||
61006                  !cimg::strcasecmp(ext,"mjp") ||
61007                  !cimg::strcasecmp(ext,"mp4") ||
61008                  !cimg::strcasecmp(ext,"mkv") ||
61009                  !cimg::strcasecmp(ext,"mpe") ||
61010                  !cimg::strcasecmp(ext,"movie") ||
61011                  !cimg::strcasecmp(ext,"ogm") ||
61012                  !cimg::strcasecmp(ext,"ogg") ||
61013                  !cimg::strcasecmp(ext,"ogv") ||
61014                  !cimg::strcasecmp(ext,"qt") ||
61015                  !cimg::strcasecmp(ext,"rm") ||
61016                  !cimg::strcasecmp(ext,"vob") ||
61017                  !cimg::strcasecmp(ext,"webm") ||
61018                  !cimg::strcasecmp(ext,"wmv") ||
61019                  !cimg::strcasecmp(ext,"xvid") ||
61020                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
61021         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
61022         else is_loaded = false;
61023       } catch (CImgIOException&) { is_loaded = false; }
61024 
61025       // If nothing loaded, try to guess file format from magic number in file.
61026       if (!is_loaded && !is_stdin) {
61027         std::FILE *const file = cimg::std_fopen(filename,"rb");
61028         if (!file) {
61029           cimg::exception_mode(omode);
61030           throw CImgIOException(_cimglist_instance
61031                                 "load(): Failed to open file '%s'.",
61032                                 cimglist_instance,
61033                                 filename);
61034         }
61035 
61036         const char *const f_type = cimg::ftype(file,filename);
61037         cimg::fclose(file);
61038         is_loaded = true;
61039         try {
61040           if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
61041           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
61042           else is_loaded = false;
61043         } catch (CImgIOException&) { is_loaded = false; }
61044       }
61045 
61046       // If nothing loaded, try to load file as a single image.
61047       if (!is_loaded) {
61048         assign(1);
61049         try {
61050           _data->load(filename);
61051         } catch (CImgIOException&) {
61052           cimg::exception_mode(omode);
61053           throw CImgIOException(_cimglist_instance
61054                                 "load(): Failed to recognize format of file '%s'.",
61055                                 cimglist_instance,
61056                                 filename);
61057         }
61058       }
61059       cimg::exception_mode(omode);
61060       return *this;
61061     }
61062 
61063     //! Load a list from a file \newinstance.
61064     static CImgList<T> get_load(const char *const filename) {
61065       return CImgList<T>().load(filename);
61066     }
61067 
61068     //! Load a list from a .cimg file.
61069     /**
61070       \param filename Filename to read data from.
61071     **/
61072     CImgList<T>& load_cimg(const char *const filename) {
61073       return _load_cimg(0,filename);
61074     }
61075 
61076     //! Load a list from a .cimg file \newinstance.
61077     static CImgList<T> get_load_cimg(const char *const filename) {
61078       return CImgList<T>().load_cimg(filename);
61079     }
61080 
61081     //! Load a list from a .cimg file.
61082     /**
61083       \param file File to read data from.
61084     **/
61085     CImgList<T>& load_cimg(std::FILE *const file) {
61086       return _load_cimg(file,0);
61087     }
61088 
61089     //! Load a list from a .cimg file \newinstance.
61090     static CImgList<T> get_load_cimg(std::FILE *const file) {
61091       return CImgList<T>().load_cimg(file);
61092     }
61093 
61094     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
61095 #ifdef cimg_use_zlib
61096 #define _cimgz_load_cimg_case(Tss) { \
61097    Bytef *const cbuf = new Bytef[csiz]; \
61098    cimg::fread(cbuf,csiz,nfile); \
61099    if (is_bool) { \
61100      CImg<ucharT> raw(W*H*D*C/8); \
61101      uLongf destlen = (ulongT)raw.size(); \
61102      uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
61103      img.assign(W,H,D,C); \
61104      img._uchar2bool(raw,raw.size(),false); \
61105    } else { \
61106      CImg<Tss> raw(W,H,D,C); \
61107      uLongf destlen = (ulongT)raw.size()*sizeof(Tss); \
61108      uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
61109      if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
61110      raw.move_to(img); \
61111    } \
61112    delete[] cbuf; \
61113 }
61114 #else
61115 #define _cimgz_load_cimg_case(Tss) \
61116    throw CImgIOException(_cimglist_instance \
61117                          "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
61118                          cimglist_instance, \
61119                          filename?filename:"(FILE*)");
61120 #endif
61121 
61122 #define _cimg_load_cimg_case(Ts,Tss) \
61123       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
61124         const bool is_bool = cimg::type<Tss>::string()==cimg::type<bool>::string(); \
61125         for (unsigned int l = 0; l<N; ++l) { \
61126           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
61127           W = H = D = C = 0; csiz = 0; \
61128           if ((err = cimg_sscanf(tmp,"%u %u %u %u #%lu",&W,&H,&D,&C,&csiz))<4) \
61129             throw CImgIOException(_cimglist_instance \
61130                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
61131                                   cimglist_instance, \
61132                                   W,H,D,C,l,filename?filename:("(FILE*)")); \
61133           if (W*H*D*C>0) { \
61134             CImg<T> &img = _data[l]; \
61135             if (err==5) _cimgz_load_cimg_case(Tss) \
61136             else { \
61137               img.assign(W,H,D,C); \
61138               T *ptrd = img._data; \
61139               if (is_bool) { \
61140                 CImg<ucharT> raw; \
61141                 for (ulongT to_read = img.size(); to_read; ) { \
61142                   raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
61143                   cimg::fread(raw._data,raw._width,nfile); \
61144                   CImg<T>(ptrd,std::min((ulongT)8*raw._width,(ulongT)(img.end() - ptrd)),1,1,1,true).\
61145                     _uchar2bool(raw,raw._width,false); \
61146                   to_read-=raw._width; \
61147                 } \
61148               } else { \
61149                 CImg<Tss> raw; \
61150                 for (ulongT to_read = img.size(); to_read; ) { \
61151                   raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
61152                   cimg::fread(raw._data,raw._width,nfile); \
61153                   if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
61154                   const Tss *ptrs = raw._data; \
61155                   for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
61156                   to_read-=raw._width; \
61157                 } \
61158               } \
61159             } \
61160           } \
61161         } \
61162         loaded = true; \
61163       }
61164 
61165       if (!filename && !file)
61166         throw CImgArgumentException(_cimglist_instance
61167                                     "load_cimg(): Specified filename is (null).",
61168                                     cimglist_instance);
61169 
61170       const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
61171       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
61172       bool loaded = false, endian = cimg::endianness();
61173       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
61174       *tmp = *str_pixeltype = *str_endian = 0;
61175       unsigned int j, N = 0, W, H, D, C;
61176       unsigned long csiz;
61177       int i, err;
61178       do {
61179         j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
61180       } while (*tmp=='#' && i>=0);
61181       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
61182                         &N,str_pixeltype._data,str_endian._data);
61183       if (err<2) {
61184         if (!file) cimg::fclose(nfile);
61185         throw CImgIOException(_cimglist_instance
61186                               "load_cimg(): CImg header not found in file '%s'.",
61187                               cimglist_instance,
61188                               filename?filename:"(FILE*)");
61189       }
61190       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
61191       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
61192       assign(N);
61193       _cimg_load_cimg_case("bool",bool);
61194       _cimg_load_cimg_case("unsigned_char",unsigned char);
61195       _cimg_load_cimg_case("uchar",unsigned char);
61196       _cimg_load_cimg_case("char",char);
61197       _cimg_load_cimg_case("unsigned_short",unsigned short);
61198       _cimg_load_cimg_case("ushort",unsigned short);
61199       _cimg_load_cimg_case("short",short);
61200       _cimg_load_cimg_case("unsigned_int",unsigned int);
61201       _cimg_load_cimg_case("uint",unsigned int);
61202       _cimg_load_cimg_case("int",int);
61203       _cimg_load_cimg_case("unsigned_long",ulongT);
61204       _cimg_load_cimg_case("ulong",ulongT);
61205       _cimg_load_cimg_case("long",longT);
61206       _cimg_load_cimg_case("unsigned_int64",uint64T);
61207       _cimg_load_cimg_case("uint64",uint64T);
61208       _cimg_load_cimg_case("int64",int64T);
61209       _cimg_load_cimg_case("float",float);
61210       _cimg_load_cimg_case("double",double);
61211 
61212       if (!loaded) {
61213         if (!file) cimg::fclose(nfile);
61214         throw CImgIOException(_cimglist_instance
61215                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
61216                               cimglist_instance,
61217                               str_pixeltype._data,filename?filename:"(FILE*)");
61218       }
61219       if (!file) cimg::fclose(nfile);
61220       return *this;
61221     }
61222 
61223     //! Load a sublist list from a (non compressed) .cimg file.
61224     /**
61225       \param filename Filename to read data from.
61226       \param n0 Starting index of images to read (~0U for max).
61227       \param n1 Ending index of images to read (~0U for max).
61228       \param x0 Starting X-coordinates of image regions to read.
61229       \param y0 Starting Y-coordinates of image regions to read.
61230       \param z0 Starting Z-coordinates of image regions to read.
61231       \param c0 Starting C-coordinates of image regions to read.
61232       \param x1 Ending X-coordinates of image regions to read (~0U for max).
61233       \param y1 Ending Y-coordinates of image regions to read (~0U for max).
61234       \param z1 Ending Z-coordinates of image regions to read (~0U for max).
61235       \param c1 Ending C-coordinates of image regions to read (~0U for max).
61236     **/
61237     CImgList<T>& load_cimg(const char *const filename,
61238                            const unsigned int n0, const unsigned int n1,
61239                            const unsigned int x0, const unsigned int y0,
61240                            const unsigned int z0, const unsigned int c0,
61241                            const unsigned int x1, const unsigned int y1,
61242                            const unsigned int z1, const unsigned int c1) {
61243       return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
61244     }
61245 
61246     //! Load a sublist list from a (non compressed) .cimg file \newinstance.
61247     static CImgList<T> get_load_cimg(const char *const filename,
61248                                      const unsigned int n0, const unsigned int n1,
61249                                      const unsigned int x0, const unsigned int y0,
61250                                      const unsigned int z0, const unsigned int c0,
61251                                      const unsigned int x1, const unsigned int y1,
61252                                      const unsigned int z1, const unsigned int c1) {
61253       return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
61254     }
61255 
61256     //! Load a sub-image list from a (non compressed) .cimg file \overloading.
61257     CImgList<T>& load_cimg(std::FILE *const file,
61258                            const unsigned int n0, const unsigned int n1,
61259                            const unsigned int x0, const unsigned int y0,
61260                            const unsigned int z0, const unsigned int c0,
61261                            const unsigned int x1, const unsigned int y1,
61262                            const unsigned int z1, const unsigned int c1) {
61263       return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
61264     }
61265 
61266     //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
61267     static CImgList<T> get_load_cimg(std::FILE *const file,
61268                                      const unsigned int n0, const unsigned int n1,
61269                                      const unsigned int x0, const unsigned int y0,
61270                                      const unsigned int z0, const unsigned int c0,
61271                                      const unsigned int x1, const unsigned int y1,
61272                                      const unsigned int z1, const unsigned int c1) {
61273       return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
61274     }
61275 
61276     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
61277                             const unsigned int n0, const unsigned int n1,
61278                             const unsigned int x0, const unsigned int y0,
61279                             const unsigned int z0, const unsigned int c0,
61280                             const unsigned int x1, const unsigned int y1,
61281                             const unsigned int z1, const unsigned int c1) {
61282 #define _cimg_load_cimg_case2(Ts,Tss) \
61283       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
61284         for (unsigned int l = 0; l<=nn1; ++l) { \
61285           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
61286           W = H = D = C = 0; \
61287           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
61288             throw CImgIOException(_cimglist_instance \
61289                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
61290                                   cimglist_instance, \
61291                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
61292           if (W*H*D*C>0) { \
61293             if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
61294             else { \
61295               const unsigned int \
61296                 _nx1 = nx1==~0U?W - 1:nx1, \
61297                 _ny1 = ny1==~0U?H - 1:ny1, \
61298                 _nz1 = nz1==~0U?D - 1:nz1, \
61299                 _nc1 = nc1==~0U?C - 1:nc1; \
61300               if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
61301                 throw CImgArgumentException(_cimglist_instance \
61302                                             "load_cimg(): Invalid specified coordinates " \
61303                                             "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
61304                                             "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
61305                                             cimglist_instance, \
61306                                             n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
61307               CImg<Tss> raw(1 + _nx1 - nx0); \
61308               CImg<T> &img = _data[l - nn0]; \
61309               img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
61310               T *ptrd = img._data; \
61311               ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
61312               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
61313               for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
61314                 const ulongT skipzb = nz0*W*H*sizeof(Tss); \
61315                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
61316                 for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
61317                   const ulongT skipyb = ny0*W*sizeof(Tss); \
61318                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
61319                   for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
61320                     const ulongT skipxb = nx0*sizeof(Tss); \
61321                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
61322                     cimg::fread(raw._data,raw._width,nfile); \
61323                     if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
61324                     const Tss *ptrs = raw._data; \
61325                     for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
61326                     const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
61327                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
61328                   } \
61329                   const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
61330                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
61331                 } \
61332                 const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
61333                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
61334               } \
61335               const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
61336               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
61337             } \
61338           } \
61339         } \
61340         loaded = true; \
61341       }
61342 
61343       if (!filename && !file)
61344         throw CImgArgumentException(_cimglist_instance
61345                                     "load_cimg(): Specified filename is (null).",
61346                                     cimglist_instance);
61347       unsigned int
61348         nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
61349         nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
61350         ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
61351         nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
61352         nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
61353 
61354       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
61355       bool loaded = false, endian = cimg::endianness();
61356       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
61357       *tmp = *str_pixeltype = *str_endian = 0;
61358       unsigned int j, N, W, H, D, C;
61359       int i, err;
61360       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
61361       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
61362                         &N,str_pixeltype._data,str_endian._data);
61363       if (err<2) {
61364         if (!file) cimg::fclose(nfile);
61365         throw CImgIOException(_cimglist_instance
61366                               "load_cimg(): CImg header not found in file '%s'.",
61367                               cimglist_instance,
61368                               filename?filename:"(FILE*)");
61369       }
61370       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
61371       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
61372       nn1 = n1==~0U?N - 1:n1;
61373       if (nn1>=N)
61374         throw CImgArgumentException(_cimglist_instance
61375                                     "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
61376                                     "because file '%s' contains only %u images.",
61377                                     cimglist_instance,
61378                                     n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
61379       assign(1 + nn1 - n0);
61380       _cimg_load_cimg_case2("bool",bool);
61381       _cimg_load_cimg_case2("unsigned_char",unsigned char);
61382       _cimg_load_cimg_case2("uchar",unsigned char);
61383       _cimg_load_cimg_case2("char",char);
61384       _cimg_load_cimg_case2("unsigned_short",unsigned short);
61385       _cimg_load_cimg_case2("ushort",unsigned short);
61386       _cimg_load_cimg_case2("short",short);
61387       _cimg_load_cimg_case2("unsigned_int",unsigned int);
61388       _cimg_load_cimg_case2("uint",unsigned int);
61389       _cimg_load_cimg_case2("int",int);
61390       _cimg_load_cimg_case2("unsigned_long",ulongT);
61391       _cimg_load_cimg_case2("ulong",ulongT);
61392       _cimg_load_cimg_case2("long",longT);
61393       _cimg_load_cimg_case2("unsigned_int64",uint64T);
61394       _cimg_load_cimg_case2("uint64",uint64T);
61395       _cimg_load_cimg_case2("int64",int64T);
61396       _cimg_load_cimg_case2("float",float);
61397       _cimg_load_cimg_case2("double",double);
61398       if (!loaded) {
61399         if (!file) cimg::fclose(nfile);
61400         throw CImgIOException(_cimglist_instance
61401                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
61402                               cimglist_instance,
61403                               str_pixeltype._data,filename?filename:"(FILE*)");
61404       }
61405       if (!file) cimg::fclose(nfile);
61406       return *this;
61407     }
61408 
61409     //! Load a list from a PAR/REC (Philips) file.
61410     /**
61411       \param filename Filename to read data from.
61412     **/
61413     CImgList<T>& load_parrec(const char *const filename) {
61414       if (!filename)
61415         throw CImgArgumentException(_cimglist_instance
61416                                     "load_parrec(): Specified filename is (null).",
61417                                     cimglist_instance);
61418 
61419       CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
61420       *body = *filenamepar = *filenamerec = 0;
61421       const char *const ext = cimg::split_filename(filename,body);
61422       if (!std::strcmp(ext,"par")) {
61423         std::strncpy(filenamepar,filename,filenamepar._width - 1);
61424         cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
61425       }
61426       if (!std::strcmp(ext,"PAR")) {
61427         std::strncpy(filenamepar,filename,filenamepar._width - 1);
61428         cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
61429       }
61430       if (!std::strcmp(ext,"rec")) {
61431         std::strncpy(filenamerec,filename,filenamerec._width - 1);
61432         cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
61433       }
61434       if (!std::strcmp(ext,"REC")) {
61435         std::strncpy(filenamerec,filename,filenamerec._width - 1);
61436         cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
61437       }
61438       std::FILE *file = cimg::fopen(filenamepar,"r");
61439 
61440       // Parse header file
61441       CImgList<floatT> st_slices;
61442       CImgList<uintT> st_global;
61443       CImg<charT> line(256); *line = 0;
61444       int err;
61445       do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
61446       do {
61447         unsigned int sn,size_x,size_y,pixsize;
61448         float rs,ri,ss;
61449         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);
61450         if (err==7) {
61451           CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
61452           unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
61453           if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
61454           else {
61455             CImg<uintT> &vec = st_global[i];
61456             if (size_x>vec[0]) vec[0] = size_x;
61457             if (size_y>vec[1]) vec[1] = size_y;
61458             vec[2] = sn;
61459           }
61460           st_slices[st_slices._width - 1][7] = (float)i;
61461         }
61462       } while (err==7);
61463 
61464       // Read data
61465       std::FILE *file2 = cimg::fopen(filenamerec,"rb");
61466       cimglist_for(st_global,l) {
61467         const CImg<uintT>& vec = st_global[l];
61468         CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
61469       }
61470 
61471       cimglist_for(st_slices,l) {
61472         const CImg<floatT>& vec = st_slices[l];
61473         const unsigned int
61474           sn = (unsigned int)vec[0] - 1,
61475           pixsize = (unsigned int)vec[1],
61476           size_x = (unsigned int)vec[2],
61477           size_y = (unsigned int)vec[3],
61478           imn = (unsigned int)vec[7];
61479         const float ri = vec[4], rs = vec[5], ss = vec[6];
61480         switch (pixsize) {
61481         case 8 : {
61482           CImg<ucharT> buf(size_x,size_y);
61483           cimg::fread(buf._data,size_x*size_y,file2);
61484           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
61485           CImg<T>& img = (*this)[imn];
61486           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
61487         } break;
61488         case 16 : {
61489           CImg<ushortT> buf(size_x,size_y);
61490           cimg::fread(buf._data,size_x*size_y,file2);
61491           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
61492           CImg<T>& img = (*this)[imn];
61493           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
61494         } break;
61495         case 32 : {
61496           CImg<uintT> buf(size_x,size_y);
61497           cimg::fread(buf._data,size_x*size_y,file2);
61498           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
61499           CImg<T>& img = (*this)[imn];
61500           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
61501         } break;
61502         default :
61503           cimg::fclose(file);
61504           cimg::fclose(file2);
61505           throw CImgIOException(_cimglist_instance
61506                                 "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
61507                                 cimglist_instance,
61508                                 pixsize,filename);
61509         }
61510       }
61511       cimg::fclose(file);
61512       cimg::fclose(file2);
61513       if (!_width)
61514         throw CImgIOException(_cimglist_instance
61515                               "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
61516                               cimglist_instance,
61517                               filename);
61518       return *this;
61519     }
61520 
61521     //! Load a list from a PAR/REC (Philips) file \newinstance.
61522     static CImgList<T> get_load_parrec(const char *const filename) {
61523       return CImgList<T>().load_parrec(filename);
61524     }
61525 
61526     //! Load a list from a YUV image sequence file.
61527     /**
61528         \param filename Filename to read data from.
61529         \param size_x Width of the images.
61530         \param size_y Height of the images.
61531         \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
61532         \param first_frame Index of first image frame to read.
61533         \param last_frame Index of last image frame to read.
61534         \param step_frame Step applied between each frame.
61535         \param yuv2rgb Apply YUV to RGB transformation during reading.
61536     **/
61537     CImgList<T>& load_yuv(const char *const filename,
61538                           const unsigned int size_x, const unsigned int size_y,
61539                           const unsigned int chroma_subsampling=444,
61540                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
61541                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
61542       return _load_yuv(0,filename,size_x,size_y,chroma_subsampling,
61543                        first_frame,last_frame,step_frame,yuv2rgb);
61544     }
61545 
61546     //! Load a list from a YUV image sequence file \newinstance.
61547     static CImgList<T> get_load_yuv(const char *const filename,
61548                                     const unsigned int size_x, const unsigned int size_y=1,
61549                                     const unsigned int chroma_subsampling=444,
61550                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
61551                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
61552       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
61553                                     first_frame,last_frame,step_frame,yuv2rgb);
61554     }
61555 
61556     //! Load a list from an image sequence YUV file \overloading.
61557     CImgList<T>& load_yuv(std::FILE *const file,
61558                           const unsigned int size_x, const unsigned int size_y,
61559                           const unsigned int chroma_subsampling=444,
61560                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
61561                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
61562       return _load_yuv(file,0,size_x,size_y,chroma_subsampling,
61563                        first_frame,last_frame,step_frame,yuv2rgb);
61564     }
61565 
61566     //! Load a list from an image sequence YUV file \newinstance.
61567     static CImgList<T> get_load_yuv(std::FILE *const file,
61568                                     const unsigned int size_x, const unsigned int size_y=1,
61569                                     const unsigned int chroma_subsampling=444,
61570                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
61571                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
61572       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
61573                                     first_frame,last_frame,step_frame,yuv2rgb);
61574     }
61575 
61576     CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
61577                            const unsigned int size_x, const unsigned int size_y,
61578                            const unsigned int chroma_subsampling,
61579                            const unsigned int first_frame, const unsigned int last_frame,
61580                            const unsigned int step_frame, const bool yuv2rgb) {
61581       if (!filename && !file)
61582         throw CImgArgumentException(_cimglist_instance
61583                                     "load_yuv(): Specified filename is (null).",
61584                                     cimglist_instance);
61585       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
61586         throw CImgArgumentException(_cimglist_instance
61587                                     "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.",
61588                                     cimglist_instance,
61589                                     chroma_subsampling,filename?filename:"(FILE*)");
61590       const unsigned int
61591         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
61592         cfy = chroma_subsampling==420?2:1,
61593         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
61594         nlast_frame = first_frame<last_frame?last_frame:first_frame,
61595         nstep_frame = step_frame?step_frame:1;
61596 
61597       if (!size_x || !size_y || size_x%cfx || size_y%cfy)
61598         throw CImgArgumentException(_cimglist_instance
61599                                     "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.",
61600                                     cimglist_instance,
61601                                     size_x,size_y,filename?filename:"(FILE*)");
61602 
61603       CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2);
61604       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
61605       bool stop_flag = false;
61606       int err;
61607       if (nfirst_frame) {
61608         err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR);
61609         if (err) {
61610           if (!file) cimg::fclose(nfile);
61611           throw CImgIOException(_cimglist_instance
61612                                 "load_yuv(): File '%s' doesn't contain frame number %u.",
61613                                 cimglist_instance,
61614                                 filename?filename:"(FILE*)",nfirst_frame);
61615         }
61616       }
61617       unsigned int frame;
61618       for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
61619         YUV.get_shared_channel(0).fill(0);
61620         // *TRY* to read the luminance part, do not replace by cimg::fread!
61621         err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile);
61622         if (err!=(int)(YUV._width*YUV._height)) {
61623           stop_flag = true;
61624           if (err>0)
61625             cimg::warn(_cimglist_instance
61626                        "load_yuv(): File '%s' contains incomplete data or given image dimensions "
61627                        "(%u,%u) are incorrect.",
61628                        cimglist_instance,
61629                        filename?filename:"(FILE*)",size_x,size_y);
61630         } else {
61631           UV.fill(0);
61632           // *TRY* to read the luminance part, do not replace by cimg::fread!
61633           err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile);
61634           if (err!=(int)(UV.size())) {
61635             stop_flag = true;
61636             if (err>0)
61637               cimg::warn(_cimglist_instance
61638                          "load_yuv(): File '%s' contains incomplete data or given image dimensions "
61639                          "(%u,%u) are incorrect.",
61640                          cimglist_instance,
61641                          filename?filename:"(FILE*)",size_x,size_y);
61642           } else {
61643             const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1);
61644             ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2);
61645             const unsigned int wd = YUV._width;
61646             switch (chroma_subsampling) {
61647             case 420 :
61648               cimg_forY(UV,y) {
61649                 cimg_forX(UV,x) {
61650                   const ucharT U = *(ptrs1++), V = *(ptrs2++);
61651                   ptrd1[wd] = U; *(ptrd1)++ = U;
61652                   ptrd1[wd] = U; *(ptrd1)++ = U;
61653                   ptrd2[wd] = V; *(ptrd2)++ = V;
61654                   ptrd2[wd] = V; *(ptrd2)++ = V;
61655                 }
61656                 ptrd1+=wd; ptrd2+=wd;
61657               }
61658               break;
61659             case 422 :
61660               cimg_forXY(UV,x,y) {
61661                 const ucharT U = *(ptrs1++), V = *(ptrs2++);
61662                 *(ptrd1++) = U; *(ptrd1++) = U;
61663                 *(ptrd2++) = V; *(ptrd2++) = V;
61664               }
61665               break;
61666             default :
61667               YUV.draw_image(0,0,0,1,UV);
61668             }
61669             if (yuv2rgb) YUV.YCbCrtoRGB();
61670             insert(YUV);
61671             if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
61672           }
61673         }
61674       }
61675       if (is_empty())
61676         throw CImgIOException(_cimglist_instance
61677                               "load_yuv() : Missing data in file '%s'.",
61678                               cimglist_instance,
61679                               filename?filename:"(FILE*)");
61680       if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
61681         cimg::warn(_cimglist_instance
61682                    "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
61683                    cimglist_instance,
61684                    nlast_frame,frame - 1,filename?filename:"(FILE*)");
61685 
61686       if (!file) cimg::fclose(nfile);
61687       return *this;
61688     }
61689 
61690     //! Load an image from a video file, using OpenCV library.
61691     /**
61692       \param filename Filename, as a C-string.
61693       \param first_frame Index of the first frame to read.
61694       \param last_frame Index of the last frame to read (can be higher than the actual number of frames, e.g. '~0U').
61695       \param step_frame Step value for frame reading.
61696       \note If step_frame==0, the current video stream is forced to be released (without any frames read).
61697     **/
61698     CImgList<T>& load_video(const char *const filename,
61699                             const unsigned int first_frame=0, const unsigned int last_frame=~0U,
61700                             const unsigned int step_frame=1) {
61701 #ifndef cimg_use_opencv
61702       if (first_frame || last_frame!=~0U || step_frame>1)
61703         throw CImgArgumentException(_cimglist_instance
61704                                     "load_video() : File '%s', arguments 'first_frame', 'last_frame' "
61705                                     "and 'step_frame' requires features from the OpenCV library "
61706                                     "('-Dcimg_use_opencv' must be defined).",
61707                                     cimglist_instance,filename);
61708       return load_ffmpeg_external(filename);
61709 #else
61710       static cv::VideoCapture *captures[32] = { 0 };
61711       static CImgList<charT> filenames(32);
61712       static CImg<uintT> positions(32,1,1,1,0);
61713       static int last_used_index = -1;
61714 
61715       // Detect if a video capture already exists for the specified filename.
61716       cimg::mutex(9);
61717       int index = -1;
61718       if (filename) {
61719         if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
61720           index = last_used_index;
61721         } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
61722             index = l; break;
61723           }
61724       } else index = last_used_index;
61725       cimg::mutex(9,0);
61726 
61727       // Release stream if needed.
61728       if (!step_frame || (index>=0 && positions[index]>first_frame)) {
61729         if (index>=0) {
61730           cimg::mutex(9);
61731           captures[index]->release();
61732           delete captures[index];
61733           captures[index] = 0;
61734           positions[index] = 0;
61735           filenames[index].assign();
61736           if (last_used_index==index) last_used_index = -1;
61737           index = -1;
61738           cimg::mutex(9,0);
61739         } else
61740           if (filename)
61741             cimg::warn(_cimglist_instance
61742                        "load_video() : File '%s', no opened video stream associated with filename found.",
61743                        cimglist_instance,filename);
61744           else
61745             cimg::warn(_cimglist_instance
61746                        "load_video() : No opened video stream found.",
61747                        cimglist_instance,filename);
61748         if (!step_frame) return *this;
61749       }
61750 
61751       // Find empty slot for capturing video stream.
61752       if (index<0) {
61753         if (!filename)
61754           throw CImgArgumentException(_cimglist_instance
61755                                       "load_video(): No already open video reader found. You must specify a "
61756                                       "non-(null) filename argument for the first call.",
61757                                       cimglist_instance);
61758         else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
61759         if (index<0)
61760           throw CImgIOException(_cimglist_instance
61761                                 "load_video(): File '%s', no video reader slots available. "
61762                                 "You have to release some of your previously opened videos.",
61763                                 cimglist_instance,filename);
61764         cimg::mutex(9);
61765         captures[index] = new cv::VideoCapture(filename);
61766         positions[index] = 0;
61767         if (!captures[index]->isOpened()) {
61768           delete captures[index];
61769           captures[index] = 0;
61770           cimg::mutex(9,0);
61771           cimg::fclose(cimg::fopen(filename,"rb"));  // Check file availability
61772           throw CImgIOException(_cimglist_instance
61773                                 "load_video(): File '%s', unable to detect format of video file.",
61774                                 cimglist_instance,filename);
61775         }
61776         CImg<charT>::string(filename).move_to(filenames[index]);
61777         cimg::mutex(9,0);
61778       }
61779 
61780       cimg::mutex(9);
61781       const unsigned int nb_frames = (unsigned int)std::max(0.,captures[index]->get(_cimg_cap_prop_frame_count));
61782       cimg::mutex(9,0);
61783       assign();
61784 
61785       // Skip frames if requested.
61786       bool go_on = true;
61787       unsigned int &pos = positions[index];
61788       while (pos<first_frame) {
61789         cimg::mutex(9);
61790         if (!captures[index]->grab()) { cimg::mutex(9,0); go_on = false; break; }
61791         cimg::mutex(9,0);
61792         ++pos;
61793       }
61794 
61795       // Read and convert frames.
61796       const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame);
61797       while (go_on && pos<=_last_frame) {
61798         cv::Mat cvimg;
61799         cimg::mutex(9);
61800         if (captures[index]->read(cvimg)) { CImg<T>::_cvmat2cimg(cvimg).move_to(*this); ++pos; }
61801         else go_on = false;
61802         cimg::mutex(9,0);
61803         if (go_on)
61804           for (unsigned int i = 1; go_on && i<step_frame && pos<=_last_frame; ++i, ++pos) {
61805             cimg::mutex(9);
61806             if (!captures[index]->grab()) go_on = false;
61807             cimg::mutex(9,0);
61808           }
61809       }
61810 
61811       if (!go_on || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary
61812         cimg::mutex(9);
61813         captures[index]->release();
61814         delete captures[index];
61815         captures[index] = 0;
61816         filenames[index].assign();
61817         positions[index] = 0;
61818         index = -1;
61819         cimg::mutex(9,0);
61820       }
61821 
61822       cimg::mutex(9);
61823       last_used_index = index;
61824       cimg::mutex(9,0);
61825 
61826       if (is_empty())
61827         throw CImgIOException(_cimglist_instance
61828                               "load_video(): File '%s', unable to locate frame %u.",
61829                               cimglist_instance,filename,first_frame);
61830       return *this;
61831 #endif
61832     }
61833 
61834     //! Load an image from a video file, using OpenCV library \newinstance.
61835     static CImgList<T> get_load_video(const char *const filename,
61836                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
61837                            const unsigned int step_frame=1) {
61838       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame);
61839     }
61840 
61841     //! Load an image from a video file using the external tool 'ffmpeg'.
61842     /**
61843       \param filename Filename to read data from.
61844     **/
61845     CImgList<T>& load_ffmpeg_external(const char *const filename) {
61846       if (!filename)
61847         throw CImgArgumentException(_cimglist_instance
61848                                     "load_ffmpeg_external(): Specified filename is (null).",
61849                                     cimglist_instance);
61850       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
61851       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
61852       std::FILE *file = 0;
61853       do {
61854         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
61855                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
61856         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
61857         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
61858       } while (file);
61859       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data);
61860       cimg_snprintf(command,command._width,"\"%s\" -v -8 -i \"%s\" \"%s\"",
61861                     cimg::ffmpeg_path(),
61862                     CImg<charT>::string(filename)._system_strescape().data(),
61863                     CImg<charT>::string(filename_tmp2)._system_strescape().data());
61864       cimg::system(command, cimg::ffmpeg_path());
61865       const unsigned int omode = cimg::exception_mode();
61866       cimg::exception_mode(0);
61867       assign();
61868       unsigned int i = 1;
61869       for (bool stop_flag = false; !stop_flag; ++i) {
61870         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i);
61871         CImg<T> img;
61872         try { img.load_pnm(filename_tmp2); }
61873         catch (CImgException&) { stop_flag = true; }
61874         if (img) { img.move_to(*this); std::remove(filename_tmp2); }
61875       }
61876       cimg::exception_mode(omode);
61877       if (is_empty())
61878         throw CImgIOException(_cimglist_instance
61879                               "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
61880                               cimglist_instance,
61881                               filename);
61882       return *this;
61883     }
61884 
61885     //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
61886     static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
61887       return CImgList<T>().load_ffmpeg_external(filename);
61888     }
61889 
61890     //! Load gif file, using ImageMagick or GraphicsMagick's external tools.
61891     /**
61892       \param filename Filename to read data from.
61893     **/
61894     CImgList<T>& load_gif_external(const char *const filename) {
61895       if (!filename)
61896         throw CImgArgumentException(_cimglist_instance
61897                                     "load_gif_external(): Specified filename is (null).",
61898                                     cimglist_instance);
61899       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
61900       if (!_load_gif_external(filename,false))
61901         if (!_load_gif_external(filename,true))
61902           try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
61903       if (is_empty())
61904         throw CImgIOException(_cimglist_instance
61905                               "load_gif_external(): Failed to open file '%s'.",
61906                               cimglist_instance,filename);
61907       return *this;
61908     }
61909 
61910     CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
61911       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
61912       std::FILE *file = 0;
61913       do {
61914         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
61915                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
61916         if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data);
61917         else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data);
61918         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
61919       } while (file);
61920       if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"",
61921                                             cimg::graphicsmagick_path(),
61922                                             CImg<charT>::string(filename)._system_strescape().data(),
61923                                             CImg<charT>::string(filename_tmp)._system_strescape().data());
61924       else cimg_snprintf(command,command._width,"\"%s\" -coalesce \"%s\" \"%s.png\"",
61925                          cimg::imagemagick_path(),
61926                          CImg<charT>::string(filename)._system_strescape().data(),
61927                          CImg<charT>::string(filename_tmp)._system_strescape().data());
61928       cimg::system(command, cimg::imagemagick_path());
61929       const unsigned int omode = cimg::exception_mode();
61930       cimg::exception_mode(0);
61931       assign();
61932 
61933       // Try to read a single frame gif.
61934       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data);
61935       CImg<T> img;
61936       try { img.load_png(filename_tmp2); }
61937       catch (CImgException&) { }
61938       if (img) { img.move_to(*this); std::remove(filename_tmp2); }
61939       else { // Try to read animated gif
61940         unsigned int i = 0;
61941         for (bool stop_flag = false; !stop_flag; ++i) {
61942           if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i);
61943           else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i);
61944           try { img.load_png(filename_tmp2); }
61945           catch (CImgException&) { stop_flag = true; }
61946           if (img) { img.move_to(*this); std::remove(filename_tmp2); }
61947         }
61948       }
61949       cimg::exception_mode(omode);
61950       return *this;
61951     }
61952 
61953     //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance.
61954     static CImgList<T> get_load_gif_external(const char *const filename) {
61955       return CImgList<T>().load_gif_external(filename);
61956     }
61957 
61958     //! Load a gzipped list, using external tool 'gunzip'.
61959     /**
61960       \param filename Filename to read data from.
61961     **/
61962     CImgList<T>& load_gzip_external(const char *const filename) {
61963       if (!filename)
61964         throw CImgIOException(_cimglist_instance
61965                               "load_gzip_external(): Specified filename is (null).",
61966                               cimglist_instance);
61967       cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
61968       CImg<charT> command(1024), filename_tmp(256), body(256);
61969       const char
61970         *ext = cimg::split_filename(filename,body),
61971         *ext2 = cimg::split_filename(body,0);
61972       std::FILE *file = 0;
61973       do {
61974         if (!cimg::strcasecmp(ext,"gz")) {
61975           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
61976                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
61977           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
61978                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
61979         } else {
61980           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
61981                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
61982           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
61983                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
61984         }
61985         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
61986       } while (file);
61987       cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
61988                     cimg::gunzip_path(),
61989                     CImg<charT>::string(filename)._system_strescape().data(),
61990                     CImg<charT>::string(filename_tmp)._system_strescape().data());
61991       cimg::system(command, cimg::gunzip_path());
61992       if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
61993         cimg::fclose(cimg::fopen(filename,"r"));
61994         throw CImgIOException(_cimglist_instance
61995                               "load_gzip_external(): Failed to open file '%s'.",
61996                               cimglist_instance,
61997                               filename);
61998 
61999       } else cimg::fclose(file);
62000       load(filename_tmp);
62001       std::remove(filename_tmp);
62002       return *this;
62003     }
62004 
62005     //! Load a gzipped list, using external tool 'gunzip' \newinstance.
62006     static CImgList<T> get_load_gzip_external(const char *const filename) {
62007       return CImgList<T>().load_gzip_external(filename);
62008     }
62009 
62010     //! Load images from a TIFF file.
62011     /**
62012         \param filename Filename to read data from.
62013         \param first_frame Index of first image frame to read.
62014         \param last_frame Index of last image frame to read.
62015         \param step_frame Step applied between each frame.
62016         \param[out] voxel_size Voxel size, as stored in the filename.
62017         \param[out] description Description, as stored in the filename.
62018     **/
62019     CImgList<T>& load_tiff(const char *const filename,
62020                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62021                            const unsigned int step_frame=1,
62022                            float *const voxel_size=0,
62023                            CImg<charT> *const description=0) {
62024       const unsigned int
62025         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
62026         nstep_frame = step_frame?step_frame:1;
62027       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
62028 #ifndef cimg_use_tiff
62029       cimg::unused(voxel_size,description);
62030       if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
62031         throw CImgArgumentException(_cimglist_instance
62032                                     "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
62033                                     cimglist_instance,
62034                                     filename);
62035 
62036       return assign(CImg<T>::get_load_tiff(filename));
62037 #else
62038 #if cimg_verbosity<3
62039         TIFFSetWarningHandler(0);
62040         TIFFSetErrorHandler(0);
62041 #endif
62042       TIFF *tif = TIFFOpen(filename,"r");
62043       if (tif) {
62044         unsigned int nb_images = 0;
62045         do ++nb_images; while (TIFFReadDirectory(tif));
62046         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
62047           cimg::warn(_cimglist_instance
62048                      "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
62049                      "file '%s' contains %u image(s).",
62050                      cimglist_instance,
62051                      nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
62052 
62053         if (nfirst_frame>=nb_images) return assign();
62054         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
62055         assign(1 + (nlast_frame - nfirst_frame)/nstep_frame);
62056         TIFFSetDirectory(tif,0);
62057         cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,voxel_size,description);
62058         TIFFClose(tif);
62059       } else throw CImgIOException(_cimglist_instance
62060                                    "load_tiff(): Failed to open file '%s'.",
62061                                    cimglist_instance,
62062                                    filename);
62063       return *this;
62064 #endif
62065     }
62066 
62067     //! Load a multi-page TIFF file \newinstance.
62068     static CImgList<T> get_load_tiff(const char *const filename,
62069                                      const unsigned int first_frame=0, const unsigned int last_frame=~0U,
62070                                      const unsigned int step_frame=1,
62071                                      float *const voxel_size=0,
62072                                      CImg<charT> *const description=0) {
62073       return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description);
62074     }
62075 
62076     //@}
62077     //----------------------------------
62078     //
62079     //! \name Data Output
62080     //@{
62081     //----------------------------------
62082 
62083     //! Print information about the list on the standard output.
62084     /**
62085       \param title Label set to the information displayed.
62086       \param display_stats Tells if image statistics must be computed and displayed.
62087     **/
62088     const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
62089       unsigned int msiz = 0;
62090       cimglist_for(*this,l) msiz+=_data[l].size();
62091       msiz*=sizeof(T);
62092       const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U;
62093       CImg<charT> _title(64);
62094       if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type());
62095       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
62096                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
62097                    cimg::t_bold,cimg::t_normal,(void*)this,
62098                    cimg::t_bold,cimg::t_normal,_width,_allocated_width,
62099                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
62100                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
62101                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
62102       if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1));
62103       else std::fprintf(cimg::output(),".\n");
62104 
62105       char tmp[16] = { 0 };
62106       cimglist_for(*this,ll) {
62107         cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
62108         std::fprintf(cimg::output(),"  ");
62109         _data[ll].print(tmp,display_stats);
62110         if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output(),"  ...\n"); }
62111       }
62112       std::fflush(cimg::output());
62113       return *this;
62114     }
62115 
62116     //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
62117     /**
62118        \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
62119        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
62120        \param align Appending alignment.
62121        \note This function displays the list images of the current CImgList instance into an existing
62122          CImgDisplay window.
62123        Images of the list are appended in a single temporary image for visualization purposes.
62124        The function returns immediately.
62125     **/
62126     const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
62127       disp.display(*this,axis,align);
62128       return *this;
62129     }
62130 
62131     //! Display the current CImgList instance in a new display window.
62132     /**
62133         \param disp Display window.
62134         \param display_info Tells if image information are displayed on the standard output.
62135         \param axis Alignment axis for images viewing.
62136         \param align Appending alignment.
62137         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
62138         \param exit_on_anykey Exit function when any key is pressed.
62139         \note This function opens a new window with a specific title and displays the list images of the
62140           current CImgList instance into it.
62141         Images of the list are appended in a single temporary image for visualization purposes.
62142         The function returns when a key is pressed or the display window is closed by the user.
62143     **/
62144     const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
62145                                const char axis='x', const float align=0,
62146                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
62147       bool is_exit = false;
62148       return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
62149     }
62150 
62151     //! Display the current CImgList instance in a new display window.
62152     /**
62153       \param title Title of the opening display window.
62154       \param display_info Tells if list information must be written on standard output.
62155       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
62156       \param align Appending alignment.
62157       \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
62158       \param exit_on_anykey Exit function when any key is pressed.
62159     **/
62160     const CImgList<T>& display(const char *const title=0, const bool display_info=true,
62161                                const char axis='x', const float align=0,
62162                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
62163       CImgDisplay disp;
62164       bool is_exit = false;
62165       return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
62166     }
62167 
62168     const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles,
62169                                 const bool display_info, const char axis, const float align, unsigned int *const XYZ,
62170                                 const bool exit_on_anykey, const unsigned int orig, const bool is_first_call,
62171                                 bool &is_exit) const {
62172       if (is_empty())
62173         throw CImgInstanceException(_cimglist_instance
62174                                     "display(): Empty instance.",
62175                                     cimglist_instance);
62176       if (!disp) {
62177         if (axis=='x') {
62178           unsigned int sum_width = 0, max_height = 0;
62179           cimglist_for(*this,l) {
62180             const CImg<T> &img = _data[l];
62181             const unsigned int
62182               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
62183               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
62184             sum_width+=w;
62185             if (h>max_height) max_height = h;
62186           }
62187           disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1);
62188         } else {
62189           unsigned int max_width = 0, sum_height = 0;
62190           cimglist_for(*this,l) {
62191             const CImg<T> &img = _data[l];
62192             const unsigned int
62193               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
62194               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
62195             if (w>max_width) max_width = w;
62196             sum_height+=h;
62197           }
62198           disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1);
62199         }
62200         if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
62201       } else if (title) disp.set_title("%s",title);
62202       else if (titles) disp.set_title("%s",titles->__display()._data);
62203       const CImg<char> dtitle = CImg<char>::string(disp.title());
62204       if (display_info) print(disp.title());
62205       disp.show().flush();
62206 
62207       if (_width==1) {
62208         const unsigned int dw = disp._width, dh = disp._height;
62209         if (!is_first_call)
62210           disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false);
62211         disp.set_title("%s (%ux%ux%ux%u)",
62212                        dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
62213         _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call);
62214         if (disp.key()) is_exit = true;
62215         disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
62216       } else {
62217         bool disp_resize = !is_first_call;
62218         while (!disp.is_closed() && !is_exit) {
62219           const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true);
62220           disp_resize = true;
62221           if (s[0]<0 && !disp.wheel()) { // No selections done
62222             if (disp.button()&2) { disp.flush(); break; }
62223             is_exit = true;
62224           } else if (disp.wheel()) { // Zoom in/out
62225             const int wheel = disp.wheel();
62226             disp.set_wheel();
62227             if (!is_first_call && wheel<0) break;
62228             if (wheel>0 && _width>=4) {
62229               const unsigned int
62230                 delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)),
62231                 ind0 = (unsigned int)std::max(0,s[0] - (int)delta),
62232                 ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta);
62233               if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) {
62234                 const CImgList<T> sublist = get_shared_images(ind0,ind1);
62235                 CImgList<charT> t_sublist;
62236                 if (titles) t_sublist = titles->get_shared_images(ind0,ind1);
62237                 sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
62238                                  orig + ind0,false,is_exit);
62239               }
62240             }
62241           } else if (s[0]!=0 || s[1]!=width() - 1) {
62242             const CImgList<T> sublist = get_shared_images(s[0],s[1]);
62243             CImgList<charT> t_sublist;
62244             if (titles) t_sublist = titles->get_shared_images(s[0],s[1]);
62245             sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
62246                              orig + s[0],false,is_exit);
62247           }
62248           disp.set_title("%s",dtitle.data());
62249         }
62250       }
62251       return *this;
62252     }
62253 
62254     // [internal] Return string to describe display title.
62255     CImg<charT> __display() const {
62256       CImg<charT> res, str;
62257       cimglist_for(*this,l) {
62258         CImg<charT>::string((char*)_data[l]).move_to(str);
62259         if (l!=width() - 1) {
62260           str.resize(str._width + 1,1,1,1,0);
62261           str[str._width - 2] = ',';
62262           str[str._width - 1] = ' ';
62263         }
62264         res.append(str,'x');
62265       }
62266       if (!res) return CImg<charT>(1,1,1,1,0).move_to(res);
62267       cimg::strellipsize(res,128,false);
62268       if (_width>1) {
62269         const unsigned int l = (unsigned int)std::strlen(res);
62270         if (res._width<=l + 16) res.resize(l + 16,1,1,1,0);
62271         cimg_snprintf(res._data + l,16," (#%u)",_width);
62272       }
62273       return res;
62274     }
62275 
62276     //! Save list into a file.
62277     /**
62278       \param filename Filename to write data to.
62279       \param number When positive, represents an index added to the filename. Otherwise, no number is added.
62280       \param digits Number of digits used for adding the number to the filename.
62281     **/
62282     const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
62283       if (!filename)
62284         throw CImgArgumentException(_cimglist_instance
62285                                     "save(): Specified filename is (null).",
62286                                     cimglist_instance);
62287       // Do not test for empty instances, since .cimg format is able to manage empty instances.
62288       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
62289       const char *const ext = cimg::split_filename(filename);
62290       CImg<charT> nfilename(1024);
62291       const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
62292         filename;
62293 
62294 #ifdef cimglist_save_plugin
62295       cimglist_save_plugin(fn);
62296 #endif
62297 #ifdef cimglist_save_plugin1
62298       cimglist_save_plugin1(fn);
62299 #endif
62300 #ifdef cimglist_save_plugin2
62301       cimglist_save_plugin2(fn);
62302 #endif
62303 #ifdef cimglist_save_plugin3
62304       cimglist_save_plugin3(fn);
62305 #endif
62306 #ifdef cimglist_save_plugin4
62307       cimglist_save_plugin4(fn);
62308 #endif
62309 #ifdef cimglist_save_plugin5
62310       cimglist_save_plugin5(fn);
62311 #endif
62312 #ifdef cimglist_save_plugin6
62313       cimglist_save_plugin6(fn);
62314 #endif
62315 #ifdef cimglist_save_plugin7
62316       cimglist_save_plugin7(fn);
62317 #endif
62318 #ifdef cimglist_save_plugin8
62319       cimglist_save_plugin8(fn);
62320 #endif
62321       if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
62322       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
62323       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
62324       else if (!cimg::strcasecmp(ext,"avi") ||
62325                !cimg::strcasecmp(ext,"mov") ||
62326                !cimg::strcasecmp(ext,"asf") ||
62327                !cimg::strcasecmp(ext,"divx") ||
62328                !cimg::strcasecmp(ext,"flv") ||
62329                !cimg::strcasecmp(ext,"mpg") ||
62330                !cimg::strcasecmp(ext,"m1v") ||
62331                !cimg::strcasecmp(ext,"m2v") ||
62332                !cimg::strcasecmp(ext,"m4v") ||
62333                !cimg::strcasecmp(ext,"mjp") ||
62334                !cimg::strcasecmp(ext,"mp4") ||
62335                !cimg::strcasecmp(ext,"mkv") ||
62336                !cimg::strcasecmp(ext,"mpe") ||
62337                !cimg::strcasecmp(ext,"movie") ||
62338                !cimg::strcasecmp(ext,"ogm") ||
62339                !cimg::strcasecmp(ext,"ogg") ||
62340                !cimg::strcasecmp(ext,"ogv") ||
62341                !cimg::strcasecmp(ext,"qt") ||
62342                !cimg::strcasecmp(ext,"rm") ||
62343                !cimg::strcasecmp(ext,"vob") ||
62344                !cimg::strcasecmp(ext,"webm") ||
62345                !cimg::strcasecmp(ext,"wmv") ||
62346                !cimg::strcasecmp(ext,"xvid") ||
62347                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
62348 #ifdef cimg_use_tiff
62349       else if (!cimg::strcasecmp(ext,"tif") ||
62350           !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
62351 #endif
62352       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
62353       else {
62354         if (_width==1) _data[0].save(fn,-1);
62355         else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); }
62356       }
62357       return *this;
62358     }
62359 
62360     //! Tell if an image list can be saved as one single file.
62361     /**
62362        \param filename Filename, as a C-string.
62363        \return \c true if the file format supports multiple images, \c false otherwise.
62364     **/
62365     static bool is_saveable(const char *const filename) {
62366       const char *const ext = cimg::split_filename(filename);
62367       if (!cimg::strcasecmp(ext,"cimgz") ||
62368 #ifdef cimg_use_tiff
62369           !cimg::strcasecmp(ext,"tif") ||
62370           !cimg::strcasecmp(ext,"tiff") ||
62371 #endif
62372           !cimg::strcasecmp(ext,"yuv") ||
62373           !cimg::strcasecmp(ext,"avi") ||
62374           !cimg::strcasecmp(ext,"mov") ||
62375           !cimg::strcasecmp(ext,"asf") ||
62376           !cimg::strcasecmp(ext,"divx") ||
62377           !cimg::strcasecmp(ext,"flv") ||
62378           !cimg::strcasecmp(ext,"mpg") ||
62379           !cimg::strcasecmp(ext,"m1v") ||
62380           !cimg::strcasecmp(ext,"m2v") ||
62381           !cimg::strcasecmp(ext,"m4v") ||
62382           !cimg::strcasecmp(ext,"mjp") ||
62383           !cimg::strcasecmp(ext,"mp4") ||
62384           !cimg::strcasecmp(ext,"mkv") ||
62385           !cimg::strcasecmp(ext,"mpe") ||
62386           !cimg::strcasecmp(ext,"movie") ||
62387           !cimg::strcasecmp(ext,"ogm") ||
62388           !cimg::strcasecmp(ext,"ogg") ||
62389           !cimg::strcasecmp(ext,"ogv") ||
62390           !cimg::strcasecmp(ext,"qt") ||
62391           !cimg::strcasecmp(ext,"rm") ||
62392           !cimg::strcasecmp(ext,"vob") ||
62393           !cimg::strcasecmp(ext,"webm") ||
62394           !cimg::strcasecmp(ext,"wmv") ||
62395           !cimg::strcasecmp(ext,"xvid") ||
62396           !cimg::strcasecmp(ext,"mpeg")) return true;
62397       return false;
62398     }
62399 
62400     //! Save image sequence as a GIF animated file.
62401     /**
62402        \param filename Filename to write data to.
62403        \param fps Number of desired frames per second.
62404        \param nb_loops Number of loops (\c 0 for infinite looping).
62405     **/
62406     const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
62407                                          const unsigned int nb_loops=0) {
62408       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
62409       CImgList<charT> filenames;
62410       std::FILE *file = 0;
62411 
62412 #ifdef cimg_use_png
62413 #define _cimg_save_gif_extension "png"
62414 #else
62415 #define _cimg_save_gif_extension "ppm"
62416 #endif
62417 
62418       do {
62419         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
62420                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
62421         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data);
62422         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
62423       } while (file);
62424       cimglist_for(*this,l) {
62425         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1);
62426         CImg<charT>::string(filename_tmp2).move_to(filenames);
62427         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2);
62428         else _data[l].save(filename_tmp2);
62429       }
62430       cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u",
62431                     cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops);
62432       CImg<ucharT>::string(command).move_to(filenames,0);
62433       cimg_snprintf(command,command._width,"\"%s\"",
62434                     CImg<charT>::string(filename)._system_strescape().data());
62435       CImg<ucharT>::string(command).move_to(filenames);
62436       CImg<charT> _command = filenames>'x';
62437       cimg_for(_command,p,char) if (!*p) *p = ' ';
62438       _command.back() = 0;
62439 
62440       cimg::system(_command, cimg::imagemagick_path());
62441       file = cimg::std_fopen(filename,"rb");
62442       if (!file)
62443         throw CImgIOException(_cimglist_instance
62444                               "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
62445                               cimglist_instance,
62446                               filename);
62447       else cimg::fclose(file);
62448       cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]);
62449       return *this;
62450     }
62451 
62452     //! Save list as a YUV image sequence file.
62453     /**
62454       \param filename Filename to write data to.
62455       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
62456       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
62457     **/
62458     const CImgList<T>& save_yuv(const char *const filename=0,
62459                                 const unsigned int chroma_subsampling=444,
62460                                 const bool is_rgb=true) const {
62461       return _save_yuv(0,filename,chroma_subsampling,is_rgb);
62462     }
62463 
62464     //! Save image sequence into a YUV file.
62465     /**
62466       \param file File to write data to.
62467       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
62468       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
62469     **/
62470     const CImgList<T>& save_yuv(std::FILE *const file,
62471                                 const unsigned int chroma_subsampling=444,
62472                                 const bool is_rgb=true) const {
62473       return _save_yuv(file,0,chroma_subsampling,is_rgb);
62474     }
62475 
62476     const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename,
62477                                  const unsigned int chroma_subsampling,
62478                                  const bool is_rgb) const {
62479       if (!file && !filename)
62480         throw CImgArgumentException(_cimglist_instance
62481                                     "save_yuv(): Specified filename is (null).",
62482                                     cimglist_instance);
62483       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
62484         throw CImgArgumentException(_cimglist_instance
62485                                     "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
62486                                     cimglist_instance,
62487                                     chroma_subsampling,filename?filename:"(FILE*)");
62488       if (is_empty()) { cimg::fempty(file,filename); return *this; }
62489       const unsigned int
62490         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
62491         cfy = chroma_subsampling==420?2:1,
62492         w0 = (*this)[0]._width, h0 = (*this)[0]._height,
62493         width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy);
62494       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
62495       cimglist_for(*this,l) {
62496         const CImg<T> &frame = (*this)[l];
62497         cimg_forZ(frame,z) {
62498           CImg<ucharT> YUV;
62499           if (sizeof(T)==1 && !is_rgb &&
62500               frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3)
62501             YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true);
62502           else {
62503             YUV = frame.get_slice(z);
62504             if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0);
62505             if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0);
62506             if (is_rgb) YUV.RGBtoYCbCr();
62507           }
62508           if (chroma_subsampling==444)
62509             cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile);
62510           else {
62511             cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile);
62512             CImg<ucharT> UV = YUV.get_channels(1,2);
62513             UV.resize(UV._width/cfx,UV._height/cfy,1,2,2);
62514             cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile);
62515           }
62516         }
62517       }
62518       if (!file) cimg::fclose(nfile);
62519       return *this;
62520     }
62521 
62522     //! Save list into a .cimg file.
62523     /**
62524        \param filename Filename to write data to.
62525        \param is_compressed Tells if data compression must be enabled.
62526     **/
62527     const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
62528       return _save_cimg(0,filename,is_compressed);
62529     }
62530 
62531     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
62532       if (!file && !filename)
62533         throw CImgArgumentException(_cimglist_instance
62534                                     "save_cimg(): Specified filename is (null).",
62535                                     cimglist_instance);
62536 #ifndef cimg_use_zlib
62537       if (is_compressed)
62538         cimg::warn(_cimglist_instance
62539                    "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
62540                    "saving them uncompressed.",
62541                    cimglist_instance,
62542                    filename?filename:"(FILE*)");
62543 #endif
62544       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
62545       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
62546       const bool is_bool = ptype==cimg::type<bool>::string();
62547       if (!is_bool && std::strstr(ptype,"unsigned")==ptype)
62548         std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
62549       else
62550         std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
62551 
62552       cimglist_for(*this,l) {
62553         const CImg<T>& img = _data[l];
62554         std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
62555         if (img._data) {
62556           CImg<T> tmp;
62557           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
62558           const CImg<T>& ref = cimg::endianness()?tmp:img;
62559           bool failed_to_compress = true;
62560           if (is_compressed) {
62561 #ifdef cimg_use_zlib
62562             Bytef *cbuf = 0;
62563             uLongf csiz = 0;
62564 
62565             if (is_bool) { // Boolean data (bitwise)
62566               ulongT siz;
62567               const unsigned char *const buf = ref._bool2uchar(siz,false);
62568               csiz = siz + siz/100 + 16;
62569               cbuf = new Bytef[csiz];
62570               failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)buf,siz);
62571               if (!failed_to_compress) {
62572                 std::fprintf(nfile," #%lu\n",csiz);
62573                 cimg::fwrite(cbuf,csiz,nfile);
62574               }
62575               delete[] buf;
62576             } else { // Non-boolean data
62577               const ulongT siz = sizeof(T)*ref.size();
62578               csiz = siz + siz/100 + 16;
62579               cbuf = new Bytef[csiz];
62580               failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)ref._data,siz);
62581               if (!failed_to_compress) {
62582                 std::fprintf(nfile," #%lu\n",csiz);
62583                 cimg::fwrite(cbuf,csiz,nfile);
62584               }
62585             }
62586             if (failed_to_compress)
62587               cimg::warn(_cimglist_instance
62588                          "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
62589                          cimglist_instance,
62590                          filename?filename:"(FILE*)");
62591             delete[] cbuf;
62592 #endif
62593           }
62594           if (failed_to_compress) { // Write non-compressed
62595             std::fputc('\n',nfile);
62596             if (is_bool) { // Boolean data (bitwise)
62597               ulongT siz;
62598               const unsigned char *const buf = ref._bool2uchar(siz,false);
62599               cimg::fwrite(buf,siz,nfile);
62600               delete[] buf;
62601             } else cimg::fwrite(ref._data,ref.size(),nfile); // Non-boolean data
62602           }
62603         } else std::fputc('\n',nfile);
62604       }
62605       if (!file) cimg::fclose(nfile);
62606       return *this;
62607     }
62608 
62609     //! Save list into a .cimg file.
62610     /**
62611        \param file File to write data to.
62612        \param is_compressed Tells if data compression must be enabled.
62613     **/
62614     const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
62615       return _save_cimg(file,0,is_compressed);
62616     }
62617 
62618     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
62619                                  const unsigned int n0,
62620                                  const unsigned int x0, const unsigned int y0,
62621                                  const unsigned int z0, const unsigned int c0) const {
62622 #define _cimg_save_cimg_case(Ts,Tss) \
62623       if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
62624         for (unsigned int l = 0; l<lmax; ++l) { \
62625           j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
62626           W = H = D = C = 0; \
62627           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
62628             throw CImgIOException(_cimglist_instance \
62629                                   "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
62630                                   cimglist_instance, \
62631                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
62632           if (W*H*D*C>0) { \
62633             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
62634             else { \
62635               const CImg<T>& img = (*this)[l - n0]; \
62636               const T *ptrs = img._data; \
62637               const unsigned int \
62638                 x1 = x0 + img._width - 1, \
62639                 y1 = y0 + img._height - 1, \
62640                 z1 = z0 + img._depth - 1, \
62641                 c1 = c0 + img._spectrum - 1, \
62642                 nx1 = x1>=W?W - 1:x1, \
62643                 ny1 = y1>=H?H - 1:y1, \
62644                 nz1 = z1>=D?D - 1:z1, \
62645                 nc1 = c1>=C?C - 1:c1; \
62646               CImg<Tss> raw(1 + nx1 - x0); \
62647               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
62648               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
62649               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
62650                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
62651                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
62652                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
62653                   const unsigned int skipyb = y0*W*sizeof(Tss); \
62654                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
62655                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
62656                     const unsigned int skipxb = x0*sizeof(Tss); \
62657                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
62658                     raw.assign(ptrs, raw._width); \
62659                     ptrs+=img._width; \
62660                     if (endian) cimg::invert_endianness(raw._data,raw._width); \
62661                     cimg::fwrite(raw._data,raw._width,nfile); \
62662                     const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
62663                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
62664                   } \
62665                   const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
62666                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
62667                 } \
62668                 const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
62669                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
62670               } \
62671               const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
62672               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
62673             } \
62674           } \
62675         } \
62676         saved = true; \
62677       }
62678 
62679       if (!file && !filename)
62680         throw CImgArgumentException(_cimglist_instance
62681                                     "save_cimg(): Specified filename is (null).",
62682                                     cimglist_instance);
62683       if (is_empty())
62684         throw CImgInstanceException(_cimglist_instance
62685                                     "save_cimg(): Empty instance, for file '%s'.",
62686                                     cimglist_instance,
62687                                     filename?filename:"(FILE*)");
62688 
62689       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
62690       bool saved = false, endian = cimg::endianness();
62691       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
62692       *tmp = *str_pixeltype = *str_endian = 0;
62693       unsigned int j, N, W, H, D, C;
62694       int i, err;
62695       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
62696       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
62697       if (err<2) {
62698         if (!file) cimg::fclose(nfile);
62699         throw CImgIOException(_cimglist_instance
62700                               "save_cimg(): CImg header not found in file '%s'.",
62701                               cimglist_instance,
62702                               filename?filename:"(FILE*)");
62703       }
62704       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
62705       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
62706       const unsigned int lmax = std::min(N,n0 + _width);
62707       _cimg_save_cimg_case("bool",bool);
62708       _cimg_save_cimg_case("unsigned_char",unsigned char);
62709       _cimg_save_cimg_case("uchar",unsigned char);
62710       _cimg_save_cimg_case("char",char);
62711       _cimg_save_cimg_case("unsigned_short",unsigned short);
62712       _cimg_save_cimg_case("ushort",unsigned short);
62713       _cimg_save_cimg_case("short",short);
62714       _cimg_save_cimg_case("unsigned_int",unsigned int);
62715       _cimg_save_cimg_case("uint",unsigned int);
62716       _cimg_save_cimg_case("int",int);
62717       _cimg_save_cimg_case("unsigned_int64",uint64T);
62718       _cimg_save_cimg_case("uint64",uint64T);
62719       _cimg_save_cimg_case("int64",int64T);
62720       _cimg_save_cimg_case("float",float);
62721       _cimg_save_cimg_case("double",double);
62722       if (!saved) {
62723         if (!file) cimg::fclose(nfile);
62724         throw CImgIOException(_cimglist_instance
62725                               "save_cimg(): Unsupported data type '%s' for file '%s'.",
62726                               cimglist_instance,
62727                               filename?filename:"(FILE*)",str_pixeltype._data);
62728       }
62729       if (!file) cimg::fclose(nfile);
62730       return *this;
62731     }
62732 
62733     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
62734     /**
62735       \param filename Filename to write data to.
62736       \param n0 Starting index of images to write.
62737       \param x0 Starting X-coordinates of image regions to write.
62738       \param y0 Starting Y-coordinates of image regions to write.
62739       \param z0 Starting Z-coordinates of image regions to write.
62740       \param c0 Starting C-coordinates of image regions to write.
62741     **/
62742     const CImgList<T>& save_cimg(const char *const filename,
62743                                  const unsigned int n0,
62744                                  const unsigned int x0, const unsigned int y0,
62745                                  const unsigned int z0, const unsigned int c0) const {
62746       return _save_cimg(0,filename,n0,x0,y0,z0,c0);
62747     }
62748 
62749     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
62750     /**
62751       \param file File to write data to.
62752       \param n0 Starting index of images to write.
62753       \param x0 Starting X-coordinates of image regions to write.
62754       \param y0 Starting Y-coordinates of image regions to write.
62755       \param z0 Starting Z-coordinates of image regions to write.
62756       \param c0 Starting C-coordinates of image regions to write.
62757     **/
62758     const CImgList<T>& save_cimg(std::FILE *const file,
62759                                  const unsigned int n0,
62760                                  const unsigned int x0, const unsigned int y0,
62761                                  const unsigned int z0, const unsigned int c0) const {
62762       return _save_cimg(file,0,n0,x0,y0,z0,c0);
62763     }
62764 
62765     static void _save_empty_cimg(std::FILE *const file, const char *const filename,
62766                                 const unsigned int nb,
62767                                 const unsigned int dx, const unsigned int dy,
62768                                 const unsigned int dz, const unsigned int dc) {
62769       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
62770       const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
62771       std::fprintf(nfile,"%u %s\n",nb,pixel_type());
62772       for (unsigned int i=nb; i; --i) {
62773         std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
62774         for (ulongT off = siz; off; --off) std::fputc(0,nfile);
62775       }
62776       if (!file) cimg::fclose(nfile);
62777     }
62778 
62779     //! Save empty (non-compressed) .cimg file with specified dimensions.
62780     /**
62781         \param filename Filename to write data to.
62782         \param nb Number of images to write.
62783         \param dx Width of images in the written file.
62784         \param dy Height of images in the written file.
62785         \param dz Depth of images in the written file.
62786         \param dc Spectrum of images in the written file.
62787     **/
62788     static void save_empty_cimg(const char *const filename,
62789                                 const unsigned int nb,
62790                                 const unsigned int dx, const unsigned int dy=1,
62791                                 const unsigned int dz=1, const unsigned int dc=1) {
62792       return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
62793     }
62794 
62795     //! Save empty .cimg file with specified dimensions.
62796     /**
62797         \param file File to write data to.
62798         \param nb Number of images to write.
62799         \param dx Width of images in the written file.
62800         \param dy Height of images in the written file.
62801         \param dz Depth of images in the written file.
62802         \param dc Spectrum of images in the written file.
62803     **/
62804     static void save_empty_cimg(std::FILE *const file,
62805                                 const unsigned int nb,
62806                                 const unsigned int dx, const unsigned int dy=1,
62807                                 const unsigned int dz=1, const unsigned int dc=1) {
62808       return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
62809     }
62810 
62811     //! Save list as a TIFF file.
62812     /**
62813       \param filename Filename to write data to.
62814       \param compression_type Compression mode used to write data.
62815       \param voxel_size Voxel size, to be stored in the filename.
62816       \param description Description, to be stored in the filename.
62817       \param use_bigtiff Allow to save big tiff files (>4Gb).
62818     **/
62819     const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
62820                                  const float *const voxel_size=0, const char *const description=0,
62821                                  const bool use_bigtiff=true) const {
62822       if (!filename)
62823         throw CImgArgumentException(_cimglist_instance
62824                                     "save_tiff(): Specified filename is (null).",
62825                                     cimglist_instance);
62826       if (is_empty()) { cimg::fempty(0,filename); return *this; }
62827 
62828 #ifndef cimg_use_tiff
62829       if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
62830       else cimglist_for(*this,l) {
62831           CImg<charT> nfilename(1024);
62832           cimg::number_filename(filename,l,6,nfilename);
62833           _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
62834         }
62835 #else
62836       ulongT siz = 0;
62837       cimglist_for(*this,l) siz+=_data[l].size();
62838       const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images
62839       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
62840       if (tif) {
62841         for (unsigned int dir = 0, l = 0; l<_width; ++l) {
62842           const CImg<T>& img = (*this)[l];
62843           cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
62844         }
62845         TIFFClose(tif);
62846       } else
62847         throw CImgIOException(_cimglist_instance
62848                               "save_tiff(): Failed to open stream for file '%s'.",
62849                               cimglist_instance,
62850                               filename);
62851 #endif
62852       return *this;
62853     }
62854 
62855     //! Save list as a gzipped file, using external tool 'gzip'.
62856     /**
62857       \param filename Filename to write data to.
62858     **/
62859     const CImgList<T>& save_gzip_external(const char *const filename) const {
62860       if (!filename)
62861         throw CImgIOException(_cimglist_instance
62862                               "save_gzip_external(): Specified filename is (null).",
62863                               cimglist_instance);
62864       CImg<charT> command(1024), filename_tmp(256), body(256);
62865       const char
62866         *ext = cimg::split_filename(filename,body),
62867         *ext2 = cimg::split_filename(body,0);
62868       std::FILE *file;
62869       do {
62870         if (!cimg::strcasecmp(ext,"gz")) {
62871           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
62872                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
62873           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
62874                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
62875         } else {
62876           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
62877                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
62878           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
62879                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
62880         }
62881         if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
62882       } while (file);
62883 
62884       if (is_saveable(body)) {
62885         save(filename_tmp);
62886         cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
62887                       cimg::gzip_path(),
62888                       CImg<charT>::string(filename_tmp)._system_strescape().data(),
62889                       CImg<charT>::string(filename)._system_strescape().data());
62890         cimg::system(command, cimg::gzip_path());
62891         file = cimg::std_fopen(filename,"rb");
62892         if (!file)
62893           throw CImgIOException(_cimglist_instance
62894                                 "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
62895                                 cimglist_instance,
62896                                 filename);
62897         else cimg::fclose(file);
62898         std::remove(filename_tmp);
62899       } else {
62900         CImg<charT> nfilename(1024);
62901         cimglist_for(*this,l) {
62902           cimg::number_filename(body,l,6,nfilename);
62903           if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext);
62904           _data[l].save_gzip_external(nfilename);
62905         }
62906       }
62907       return *this;
62908     }
62909 
62910     //! Save image sequence (using the OpenCV library when available).
62911     /**
62912        \param filename Filename to write data to.
62913        \param fps Number of frames per second.
62914        \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
62915        \param keep_open Tells if the video writer associated to the specified filename
62916        must be kept open or not (to allow frames to be added in the same file afterwards).
62917     **/
62918     const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
62919                                   const char *codec=0, const bool keep_open=false) const {
62920 #ifndef cimg_use_opencv
62921       cimg::unused(codec,keep_open);
62922       return save_ffmpeg_external(filename,fps);
62923 #else
62924       try {
62925         static cv::VideoWriter *writers[32] = { 0 };
62926         static CImgList<charT> filenames(32);
62927         static CImg<intT> sizes(32,2,1,1,0);
62928         static int last_used_index = -1;
62929 
62930         // Detect if a video writer already exists for the specified filename.
62931         cimg::mutex(9);
62932         int index = -1;
62933         if (filename) {
62934           if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
62935             index = last_used_index;
62936           } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
62937               index = l; break;
62938             }
62939         } else index = last_used_index;
62940         cimg::mutex(9,0);
62941 
62942         // Find empty slot for capturing video stream.
62943         if (index<0) {
62944           if (!filename)
62945             throw CImgArgumentException(_cimglist_instance
62946                                         "save_video(): No already open video writer found. You must specify a "
62947                                         "non-(null) filename argument for the first call.",
62948                                         cimglist_instance);
62949           else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
62950           if (index<0)
62951             throw CImgIOException(_cimglist_instance
62952                                   "save_video(): File '%s', no video writer slots available. "
62953                                   "You have to release some of your previously opened videos.",
62954                                   cimglist_instance,filename);
62955           if (is_empty())
62956             throw CImgInstanceException(_cimglist_instance
62957                                         "save_video(): Instance list is empty.",
62958                                         cimglist_instance);
62959           const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
62960           if (!W || !H)
62961             throw CImgInstanceException(_cimglist_instance
62962                                         "save_video(): Frame [0] is an empty image.",
62963                                         cimglist_instance);
62964           const char
62965             *const _codec = codec && *codec?codec:"h264",
62966             codec0 = cimg::uppercase(_codec[0]),
62967             codec1 = _codec[0]?cimg::uppercase(_codec[1]):0,
62968             codec2 = _codec[1]?cimg::uppercase(_codec[2]):0,
62969             codec3 = _codec[2]?cimg::uppercase(_codec[3]):0;
62970           cimg::mutex(9);
62971           writers[index] = new cv::VideoWriter(filename,_cimg_fourcc(codec0,codec1,codec2,codec3),fps,cv::Size(W,H));
62972           if (!writers[index]->isOpened()) {
62973             delete writers[index];
62974             writers[index] = 0;
62975             cimg::mutex(9,0);
62976             throw CImgIOException(_cimglist_instance
62977                                   "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
62978                                   cimglist_instance,filename,
62979                                   codec0,codec1,codec2,codec3);
62980           }
62981           CImg<charT>::string(filename).move_to(filenames[index]);
62982           sizes(index,0) = W;
62983           sizes(index,1) = H;
62984           cimg::mutex(9,0);
62985         }
62986 
62987         if (!is_empty()) {
62988           const unsigned int W = sizes(index,0), H = sizes(index,1);
62989           cimg::mutex(9);
62990           cimglist_for(*this,l) {
62991             CImg<T> &src = _data[l];
62992             if (src.is_empty())
62993               cimg::warn(_cimglist_instance
62994                          "save_video(): Skip empty frame %d for file '%s'.",
62995                          cimglist_instance,l,filename);
62996             if (src._depth>1 || src._spectrum>3)
62997               cimg::warn(_cimglist_instance
62998                          "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
62999                          "Some image data may be ignored when writing frame into video file '%s'.",
63000                          cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
63001             if (src._width==W && src._height==H && src._spectrum==3)
63002               writers[index]->write(CImg<ucharT>(src)._cimg2cvmat());
63003             else {
63004               CImg<ucharT> _src(src,false);
63005               _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H);
63006               _src.resize(W,H,1,3,_src._spectrum==1);
63007               writers[index]->write(_src._cimg2cvmat());
63008             }
63009           }
63010           cimg::mutex(9,0);
63011         }
63012 
63013         cimg::mutex(9);
63014         if (!keep_open) {
63015           delete writers[index];
63016           writers[index] = 0;
63017           filenames[index].assign();
63018           sizes(index,0) = sizes(index,1) = 0;
63019           last_used_index = -1;
63020         } else last_used_index = index;
63021         cimg::mutex(9,0);
63022       } catch (CImgIOException &e) {
63023         if (!keep_open) return save_ffmpeg_external(filename,fps);
63024         throw e;
63025       }
63026       return *this;
63027 #endif
63028     }
63029 
63030     //! Save image sequence, using the external tool 'ffmpeg'.
63031     /**
63032       \param filename Filename to write data to.
63033       \param fps Number of frames per second.
63034       \param codec Type of compression.
63035       \param bitrate Output bitrate
63036     **/
63037     const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
63038                                             const char *const codec=0, const unsigned int bitrate=2048) const {
63039       if (!filename)
63040         throw CImgArgumentException(_cimglist_instance
63041                                     "save_ffmpeg_external(): Specified filename is (null).",
63042                                     cimglist_instance);
63043       if (is_empty()) { cimg::fempty(0,filename); return *this; }
63044 
63045       const char
63046         *const ext = cimg::split_filename(filename),
63047         *const _codec = codec?codec:
63048         !cimg::strcasecmp(ext,"flv")?"flv":
63049         !cimg::strcasecmp(ext,"mp4")?"h264":"mpeg2video";
63050 
63051       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
63052       CImgList<charT> filenames;
63053       std::FILE *file = 0;
63054       cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
63055         throw CImgInstanceException(_cimglist_instance
63056                                     "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
63057                                     cimglist_instance,
63058                                     filename);
63059       do {
63060         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
63061                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
63062         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
63063         if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
63064       } while (file);
63065       cimglist_for(*this,l) {
63066         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1);
63067         CImg<charT>::string(filename_tmp2).move_to(filenames);
63068         CImg<T> tmp = _data[l].get_shared();
63069         if (tmp._width%2 || tmp._height%2) // Force output to have an even number of columns and rows
63070           tmp.assign(tmp.get_resize(tmp._width + (tmp._width%2),tmp._height + (tmp._height%2),1,-100,0),false);
63071         if (tmp._depth>1 || tmp._spectrum!=3) // Force output to be one slice, in color
63072           tmp.assign(tmp.get_resize(-100,-100,1,3),false);
63073         tmp.save_pnm(filename_tmp2);
63074       }
63075       cimg_snprintf(command,command._width,
63076                     "\"%s\" -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"",
63077                     cimg::ffmpeg_path(),
63078                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
63079                     _codec,bitrate,fps,
63080                     CImg<charT>::string(filename)._system_strescape().data());
63081       cimg::system(command, cimg::ffmpeg_path());
63082       file = cimg::std_fopen(filename,"rb");
63083       if (!file)
63084         throw CImgIOException(_cimglist_instance
63085                               "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
63086                               cimglist_instance,
63087                               filename);
63088       else cimg::fclose(file);
63089       cimglist_for(*this,l) std::remove(filenames[l]);
63090       return *this;
63091     }
63092 
63093     //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
63094     /**
63095        \param is_compressed tells if zlib compression must be used for serialization
63096        (this requires 'cimg_use_zlib' been enabled).
63097     **/
63098     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
63099 #ifndef cimg_use_zlib
63100       if (is_compressed)
63101         cimg::warn(_cimglist_instance
63102                    "get_serialize(): Unable to compress data unless zlib is enabled, "
63103                    "storing them uncompressed.",
63104                    cimglist_instance);
63105 #endif
63106       CImgList<ucharT> stream;
63107       CImg<charT> tmpstr(128);
63108       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
63109       if (std::strstr(ptype,"unsigned")==ptype)
63110         cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
63111       else
63112         cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
63113       CImg<ucharT>::string(tmpstr,false).move_to(stream);
63114       cimglist_for(*this,l) {
63115         const CImg<T>& img = _data[l];
63116         cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
63117         CImg<ucharT>::string(tmpstr,false).move_to(stream);
63118         if (img._data) {
63119           CImg<T> tmp;
63120           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
63121           const CImg<T>& ref = cimg::endianness()?tmp:img;
63122           bool failed_to_compress = true;
63123           if (is_compressed) {
63124 #ifdef cimg_use_zlib
63125             const ulongT siz = sizeof(T)*ref.size();
63126             uLongf csiz = (ulongT)compressBound(siz);
63127             Bytef *const cbuf = new Bytef[csiz];
63128             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
63129               cimg::warn(_cimglist_instance
63130                          "get_serialize(): Failed to save compressed data, saving them uncompressed.",
63131                          cimglist_instance);
63132             else {
63133               cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
63134               CImg<ucharT>::string(tmpstr,false).move_to(stream);
63135               CImg<ucharT>(cbuf,csiz).move_to(stream);
63136               delete[] cbuf;
63137               failed_to_compress = false;
63138             }
63139 #endif
63140           }
63141           if (failed_to_compress) { // Write in a non-compressed way
63142             CImg<charT>::string("\n",false).move_to(stream);
63143             stream.insert(1);
63144             stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true);
63145           }
63146         } else CImg<charT>::string("\n",false).move_to(stream);
63147       }
63148       cimglist_apply(stream,unroll)('y');
63149       return stream>'y';
63150     }
63151 
63152     //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
63153     template<typename t>
63154     static CImgList<T> get_unserialize(const CImg<t>& buffer) {
63155 #ifdef cimg_use_zlib
63156 #define _cimgz_unserialize_case(Tss) { \
63157         Bytef *cbuf = 0; \
63158         if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type<bool>::string()) { \
63159           cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
63160           for (ulongT k = 0; k<csiz; ++k) *(_cbuf++) = (Bytef)*(stream++); \
63161           is_bytef = false; \
63162         } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \
63163         raw.assign(W,H,D,C); \
63164         uLongf destlen = raw.size()*sizeof(Tss); \
63165         uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
63166         if (!is_bytef) delete[] cbuf; \
63167       }
63168 #else
63169 #define _cimgz_unserialize_case(Tss) \
63170       throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
63171                                   "unless zlib is enabled.", \
63172                                   pixel_type());
63173 #endif
63174 
63175 #define _cimg_unserialize_case(Ts,Tss) \
63176       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
63177         for (unsigned int l = 0; l<N; ++l) { \
63178           j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
63179           ++stream; tmp[j] = 0; \
63180           W = H = D = C = 0; csiz = 0; \
63181           if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
63182             throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
63183                                         "image #%u in serialized buffer.", \
63184                                         pixel_type(),W,H,D,C,l); \
63185           if (W*H*D*C>0) { \
63186             CImg<Tss> raw; \
63187             CImg<T> &img = res._data[l]; \
63188             if (err==5) _cimgz_unserialize_case(Tss) \
63189             else { \
63190               raw.assign(W,H,D,C); \
63191               CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \
63192               if (sizeof(t)==1) { std::memcpy(_raw,stream,_raw.size()); stream+=_raw.size(); } \
63193               else cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \
63194             } \
63195             if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
63196             raw.move_to(img); \
63197           } \
63198         } \
63199         loaded = true; \
63200       }
63201 
63202       if (buffer.is_empty())
63203         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
63204                                     pixel_type());
63205       CImgList<T> res;
63206       const t *stream = buffer._data, *const estream = buffer._data + buffer.size();
63207       bool loaded = false, endian = cimg::endianness(), is_bytef = false;
63208       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
63209       *tmp = *str_pixeltype = *str_endian = 0;
63210       unsigned int j, N = 0, W, H, D, C;
63211       uint64T csiz;
63212       int i, err;
63213       cimg::unused(is_bytef);
63214       do {
63215         j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
63216         ++stream; tmp[j] = 0;
63217       } while (*tmp=='#' && stream<estream);
63218       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
63219                         &N,str_pixeltype._data,str_endian._data);
63220       if (err<2)
63221         throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
63222                                     pixel_type());
63223       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
63224       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
63225       res.assign(N);
63226       _cimg_unserialize_case("bool",bool);
63227       _cimg_unserialize_case("unsigned_char",unsigned char);
63228       _cimg_unserialize_case("uchar",unsigned char);
63229       _cimg_unserialize_case("char",char);
63230       _cimg_unserialize_case("unsigned_short",unsigned short);
63231       _cimg_unserialize_case("ushort",unsigned short);
63232       _cimg_unserialize_case("short",short);
63233       _cimg_unserialize_case("unsigned_int",unsigned int);
63234       _cimg_unserialize_case("uint",unsigned int);
63235       _cimg_unserialize_case("int",int);
63236       _cimg_unserialize_case("unsigned_int64",uint64T);
63237       _cimg_unserialize_case("uint64",uint64T);
63238       _cimg_unserialize_case("int64",int64T);
63239       _cimg_unserialize_case("float",float);
63240       _cimg_unserialize_case("double",double);
63241       if (!loaded)
63242         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
63243                                     "in serialized buffer.",
63244                                     pixel_type(),str_pixeltype._data);
63245       return res;
63246     }
63247 
63248     //@}
63249     //----------------------------------
63250     //
63251     //! \name Others
63252     //@{
63253     //----------------------------------
63254 
63255     //! Return a CImg pre-defined font with requested height.
63256     /**
63257        \param font_height Height of the desired font (exact match for 13,23,53,103).
63258        \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
63259     **/
63260     static const CImgList<ucharT>& font(const unsigned int requested_height, const bool is_variable_width=true) {
63261       if (!requested_height) return CImgList<ucharT>::const_empty();
63262       cimg::mutex(11);
63263       static const unsigned char font_resizemap[] = {
63264         0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30,
63265         32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52,
63266         54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72,
63267         73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
63268         90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
63269         107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
63270         123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
63271         138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
63272         152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165,
63273         166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179,
63274         180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192,
63275         193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205,
63276         206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218,
63277         219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231,
63278         231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243,
63279         244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 };
63280       static const char *const *font_data[] = {
63281         cimg::data_font_small,
63282         cimg::data_font_normal,
63283         cimg::data_font_large,
63284         cimg::data_font_huge };
63285       static const unsigned int
63286         font_width[] = { 10,26,52,104 },
63287         font_height[] = { 13,32,64,128 },
63288         font_M[] = { 86,91,91,47 },
63289         font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*),
63290                          sizeof(cimg::data_font_normal)/sizeof(char*),
63291                          sizeof(cimg::data_font_large)/sizeof(char*),
63292                          sizeof(cimg::data_font_huge)/sizeof(char*) };
63293       static const unsigned char font_is_binary[] = { 1,0,0,1 };
63294       static CImg<ucharT> font_base[4];
63295 
63296       unsigned int ind =
63297         requested_height<=font_height[0]?0U:
63298         requested_height<=font_height[1]?1U:
63299         requested_height<=font_height[2]?2U:3U;
63300 
63301       // Decompress nearest base font data if needed.
63302       CImg<ucharT> &basef = font_base[ind];
63303       if (!basef) {
63304         basef.assign(256*font_width[ind],font_height[ind]);
63305 
63306         unsigned char *ptrd = basef;
63307         const unsigned char *const ptrde = basef.end();
63308 
63309         // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb).
63310         CImg<char> dataf;
63311         for (unsigned int k = 0; k<font_chunk[ind]; ++k)
63312           dataf.append(CImg<char>::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x');
63313 
63314         // Uncompress font data (decode RLE).
63315         const unsigned int M = font_M[ind];
63316         if (font_is_binary[ind])
63317           for (const char *ptrs = dataf; *ptrs; ++ptrs) {
63318             const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n;
63319             if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
63320             else { std::memset(ptrd,v,ptrde - ptrd); break; }
63321           }
63322         else
63323           for (const char *ptrs = dataf; *ptrs; ++ptrs) {
63324             int n = (int)*ptrs - M - 32, v = 0;
63325             if (n>=0) { v = 85*n; n = 1; }
63326             else {
63327               n = -n;
63328               v = (int)*(++ptrs) - M - 32;
63329               if (v<0) { v = 0; --ptrs; } else v*=85;
63330             }
63331             if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
63332             else { std::memset(ptrd,v,ptrde - ptrd); break; }
63333           }
63334       }
63335 
63336       // Find optimal font cache location to return.
63337       static CImgList<ucharT> fonts[16];
63338       static bool is_variable_widths[16] = { 0 };
63339       ind = ~0U;
63340       for (int i = 0; i<16; ++i)
63341         if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) {
63342           ind = (unsigned int)i; break; // Found empty slot or cached font
63343         }
63344       if (ind==~0U) { // No empty slots nor existing font in cache
63345         fonts->assign();
63346         std::memmove((void*)fonts,(void*)(fonts + 1),15*sizeof(CImgList<ucharT>));
63347         std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
63348         std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font
63349       }
63350       CImgList<ucharT> &font = fonts[ind];
63351 
63352       // Render requested font.
63353       if (!font) {
63354         is_variable_widths[ind] = is_variable_width;
63355         basef.get_split('x',256).move_to(font);
63356         if (requested_height!=font[0]._height)
63357           cimglist_for(font,l) {
63358             font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,
63359                            font[0]._height>requested_height?2:5);
63360             cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr];
63361           }
63362         if (is_variable_width) { // Crop font
63363           cimglist_for(font,l) {
63364             CImg<ucharT>& letter = font[l];
63365             int xmin = letter.width(), xmax = 0;
63366             cimg_forX(letter,x) { // Find xmin
63367               cimg_forY(letter,y) if (letter(x,y)) { xmin = x; break; }
63368               if (xmin!=letter.width()) break;
63369             }
63370             cimg_rofX(letter,x) { // Find xmax
63371               cimg_forY(letter,y) if (letter(x,y)) { xmax = x; break; }
63372               if (xmax) break;
63373             }
63374             if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1);
63375           }
63376           font[(int)' '].resize(font[(int)'f']._width,-100,-100,-100,0);
63377           if (' ' + 256<font.size()) font[' ' + 256].resize(font[(int)'f']._width,-100,-100,-100,0);
63378         }
63379         font.insert(256,0);
63380         cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,3,1);
63381       }
63382       cimg::mutex(11,0);
63383       return font;
63384     }
63385 
63386     //! Compute a 1D Fast Fourier Transform, along specified axis.
63387     /**
63388        \param axis Axis along which the Fourier transform is computed.
63389        \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
63390     **/
63391     CImgList<T>& FFT(const char axis, const bool invert=false) {
63392       if (is_empty()) return *this;
63393       if (_width==1) insert(1);
63394       if (_width>2)
63395         cimg::warn(_cimglist_instance
63396                    "FFT(): Instance has more than 2 images",
63397                    cimglist_instance);
63398       CImg<T>::FFT(_data[0],_data[1],axis,invert);
63399       return *this;
63400     }
63401 
63402     //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
63403     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
63404       return CImgList<Tfloat>(*this,false).FFT(axis,invert);
63405     }
63406 
63407     //! Compute n-D Fast Fourier Transform.
63408     /**
63409       \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
63410     **/
63411     CImgList<T>& FFT(const bool invert=false) {
63412       if (is_empty()) return *this;
63413       if (_width==1) insert(1);
63414       if (_width>2)
63415         cimg::warn(_cimglist_instance
63416                    "FFT(): Instance has more than 2 images",
63417                    cimglist_instance);
63418 
63419       CImg<T>::FFT(_data[0],_data[1],invert);
63420       return *this;
63421     }
63422 
63423     //! Compute n-D Fast Fourier Transform \newinstance.
63424     CImgList<Tfloat> get_FFT(const bool invert=false) const {
63425       return CImgList<Tfloat>(*this,false).FFT(invert);
63426     }
63427 
63428     //! Reverse primitives orientations of a 3D object.
63429     /**
63430     **/
63431     CImgList<T>& reverse_object3d() {
63432       cimglist_for(*this,l) {
63433         CImg<T>& p = _data[l];
63434         switch (p.size()) {
63435         case 2 : case 3: cimg::swap(p[0],p[1]); break;
63436         case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
63437         case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
63438         case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
63439         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;
63440         }
63441       }
63442       return *this;
63443     }
63444 
63445     //! Reverse primitives orientations of a 3D object \newinstance.
63446     CImgList<T> get_reverse_object3d() const {
63447       return (+*this).reverse_object3d();
63448     }
63449 
63450     //@}
63451   }; // struct CImgList { ...
63452 
63453   // Completion of previously declared functions
63454   //--------------------------------------------
63455   namespace cimg {
63456 
63457     // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
63458     // (throw a CImgIOException when macro 'cimg_use_r' is defined).
63459     inline FILE* _stdin(const bool throw_exception) {
63460 #ifndef cimg_use_r
63461       cimg::unused(throw_exception);
63462       return stdin;
63463 #else
63464       if (throw_exception) {
63465         cimg::exception_mode(0);
63466         throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
63467                               "('cimg_use_r' is defined).");
63468       }
63469       return 0;
63470 #endif
63471     }
63472 
63473     inline FILE* _stdout(const bool throw_exception) {
63474 #ifndef cimg_use_r
63475       cimg::unused(throw_exception);
63476       return stdout;
63477 #else
63478       if (throw_exception) {
63479         cimg::exception_mode(0);
63480         throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
63481                               "('cimg_use_r' is defined).");
63482       }
63483       return 0;
63484 #endif
63485     }
63486 
63487     inline FILE* _stderr(const bool throw_exception) {
63488 #ifndef cimg_use_r
63489       cimg::unused(throw_exception);
63490       return stderr;
63491 #else
63492       if (throw_exception) {
63493         cimg::exception_mode(0);
63494         throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
63495                               "('cimg_use_r' is defined).");
63496       }
63497       return 0;
63498 #endif
63499     }
63500 
63501     // Open a file (similar to std:: fopen(), but with wide character support on Windows).
63502     inline std::FILE *std_fopen(const char *const path, const char *const mode) {
63503       std::FILE *const res = std::fopen(path,mode);
63504       if (res) return res;
63505 #if cimg_OS==2
63506       // Try alternative method, with wide-character string.
63507       int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
63508       if (err) {
63509         CImg<wchar_t> wpath(err);
63510         err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
63511         if (err) { // Convert 'mode' to a wide-character string
63512           err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
63513           if (err) {
63514             CImg<wchar_t> wmode(err);
63515             if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err))
63516               return _wfopen(wpath,wmode);
63517           }
63518         }
63519       }
63520 #endif
63521       return 0;
63522     }
63523 
63524     //! Search path of an executable (Windows only).
63525 #if cimg_OS==2
63526     inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) {
63527       char *ptr = 0;
63528       DWORD err = SearchPathA(0,exec_name,0,size_res,res,&ptr);
63529       return err!=0;
63530     }
63531 #endif
63532 
63533     //! Get the file or directory attributes with support for UTF-8 paths (Windows only).
63534 #if cimg_OS==2
63535     inline DWORD win_getfileattributes(const char *const path) {
63536       DWORD res = GetFileAttributesA(path);
63537       if (res==INVALID_FILE_ATTRIBUTES) {
63538         // Try alternative method, with wide-character string.
63539         int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
63540         if (err) {
63541           CImg<wchar_t> wpath(err);
63542           if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath);
63543         }
63544       }
63545       return res;
63546     }
63547 #endif
63548 
63549     //! Get/set path to the <i>Program Files/</i> directory (Windows only).
63550     /**
63551        \param user_path Specified path, or \c 0 to get the path currently used.
63552        \param reinit_path Force path to be recalculated (may take some time).
63553        \return Path containing the program files.
63554     **/
63555 #if cimg_OS==2
63556     inline const char* win_programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
63557       static CImg<char> s_path;
63558       cimg::mutex(7);
63559       if (reinit_path) s_path.assign();
63560       if (user_path) {
63561         if (!s_path) s_path.assign(1024);
63562         std::strncpy(s_path,user_path,1023);
63563       } else if (!s_path) {
63564         s_path.assign(MAX_PATH);
63565         *s_path = 0;
63566         // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
63567 #if !defined(__INTEL_COMPILER)
63568         if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
63569           const char *const pfPath = std::getenv("PROGRAMFILES");
63570           if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1);
63571           else std::strcpy(s_path,"C:\\PROGRA~1");
63572         }
63573 #else
63574         std::strcpy(s_path,"C:\\PROGRA~1");
63575 #endif
63576       }
63577       cimg::mutex(7,0);
63578       return s_path;
63579     }
63580 #endif
63581 
63582     //! Get/set path to the \c curl binary.
63583     /**
63584        \param user_path Specified path, or \c 0 to get the path currently used.
63585        \param reinit_path Force path to be recalculated (may take some time).
63586        \return Path containing the \c curl binary.
63587     **/
63588     inline const char *curl_path(const char *const user_path, const bool reinit_path) {
63589       static CImg<char> s_path;
63590       cimg::mutex(7);
63591       if (reinit_path) s_path.assign();
63592       if (user_path) {
63593         if (!s_path) s_path.assign(1024);
63594         std::strncpy(s_path,user_path,1023);
63595       } else if (!s_path) {
63596         s_path.assign(1024);
63597         bool path_found = false;
63598         std::FILE *file = 0;
63599 #if cimg_OS==2
63600         if (win_searchpath("curl.exe",s_path,s_path._width)) path_found = true;
63601         if (!path_found) {
63602           std::strcpy(s_path,".\\curl.exe");
63603           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63604         }
63605         if (!path_found) std::strcpy(s_path,"curl.exe");
63606 #else
63607         if (!path_found) {
63608           std::strcpy(s_path,"./curl");
63609           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63610         }
63611         if (!path_found) std::strcpy(s_path,"curl");
63612 #endif
63613         winformat_string(s_path);
63614       }
63615       cimg::mutex(7,0);
63616       return s_path;
63617     }
63618 
63619     //! Get/set path to the \c dcraw binary.
63620     /**
63621        \param user_path Specified path, or \c 0 to get the path currently used.
63622        \param reinit_path Force path to be recalculated (may take some time).
63623        \return Path containing the \c dcraw binary.
63624     **/
63625     inline const char *dcraw_path(const char *const user_path, const bool reinit_path) {
63626       static CImg<char> s_path;
63627       cimg::mutex(7);
63628       if (reinit_path) s_path.assign();
63629       if (user_path) {
63630         if (!s_path) s_path.assign(1024);
63631         std::strncpy(s_path,user_path,1023);
63632       } else if (!s_path) {
63633         s_path.assign(1024);
63634         bool path_found = false;
63635         std::FILE *file = 0;
63636 #if cimg_OS==2
63637         if (win_searchpath("dcraw.exe",s_path,s_path._width)) path_found = true;
63638         if (!path_found) {
63639           std::strcpy(s_path,".\\dcraw.exe");
63640           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63641         }
63642         if (!path_found) std::strcpy(s_path,"dcraw.exe");
63643 #else
63644         if (!path_found) {
63645           std::strcpy(s_path,"./dcraw");
63646           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63647         }
63648         if (!path_found) std::strcpy(s_path,"dcraw");
63649 #endif
63650         winformat_string(s_path);
63651       }
63652       cimg::mutex(7,0);
63653       return s_path;
63654     }
63655 
63656     //! Get/set path to the FFMPEG's \c ffmpeg binary.
63657     /**
63658        \param user_path Specified path, or \c 0 to get the path currently used.
63659        \param reinit_path Force path to be recalculated (may take some time).
63660        \return Path containing the \c ffmpeg binary.
63661     **/
63662     inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) {
63663       static CImg<char> s_path;
63664       cimg::mutex(7);
63665       if (reinit_path) s_path.assign();
63666       if (user_path) {
63667         if (!s_path) s_path.assign(1024);
63668         std::strncpy(s_path,user_path,1023);
63669       } else if (!s_path) {
63670         s_path.assign(1024);
63671         bool path_found = false;
63672         std::FILE *file = 0;
63673 #if cimg_OS==2
63674         if (win_searchpath("ffmpeg.exe",s_path,s_path._width)) path_found = true;
63675         if (!path_found) {
63676           std::strcpy(s_path,".\\ffmpeg.exe");
63677           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63678         }
63679         if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
63680 #else
63681         if (!path_found) {
63682           std::strcpy(s_path,"./ffmpeg");
63683           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63684         }
63685         if (!path_found) std::strcpy(s_path,"ffmpeg");
63686 #endif
63687         winformat_string(s_path);
63688       }
63689       cimg::mutex(7,0);
63690       return s_path;
63691     }
63692 
63693     //! Get/set path to the GraphicsMagick's \c gm binary.
63694     /**
63695        \param user_path Specified path, or \c 0 to get the path currently used.
63696        \param reinit_path Force path to be recalculated (may take some time).
63697        \return Path containing the \c gm binary.
63698     **/
63699     inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) {
63700       static CImg<char> s_path;
63701       cimg::mutex(7);
63702       if (reinit_path) s_path.assign();
63703       if (user_path) {
63704         if (!s_path) s_path.assign(1024);
63705         std::strncpy(s_path,user_path,1023);
63706       } else if (!s_path) {
63707         s_path.assign(1024);
63708         bool path_found = false;
63709         std::FILE *file = 0;
63710 #if cimg_OS==2
63711         if (win_searchpath("gm.exe",s_path,s_path._width)) path_found = true;
63712         const char *const pf_path = win_programfiles_path();
63713         if (!path_found) {
63714           std::strcpy(s_path,".\\gm.exe");
63715           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63716         }
63717         for (int k = 32; k>=10 && !path_found; --k) {
63718           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
63719           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63720         }
63721         for (int k = 9; k>=0 && !path_found; --k) {
63722           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
63723           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63724         }
63725         for (int k = 32; k>=0 && !path_found; --k) {
63726           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
63727           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63728         }
63729         for (int k = 32; k>=10 && !path_found; --k) {
63730           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
63731           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63732         }
63733         for (int k = 9; k>=0 && !path_found; --k) {
63734           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
63735           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63736         }
63737         for (int k = 32; k>=0 && !path_found; --k) {
63738           cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
63739           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63740         }
63741         for (int k = 32; k>=10 && !path_found; --k) {
63742           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
63743           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63744         }
63745         for (int k = 9; k>=0 && !path_found; --k) {
63746           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
63747           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63748         }
63749         for (int k = 32; k>=0 && !path_found; --k) {
63750           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k);
63751           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63752         }
63753         for (int k = 32; k>=10 && !path_found; --k) {
63754           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
63755           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63756         }
63757         for (int k = 9; k>=0 && !path_found; --k) {
63758           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
63759           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63760         }
63761         for (int k = 32; k>=0 && !path_found; --k) {
63762           cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
63763           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63764         }
63765         for (int k = 32; k>=10 && !path_found; --k) {
63766           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
63767           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63768         }
63769         for (int k = 9; k>=0 && !path_found; --k) {
63770           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
63771           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63772         }
63773         for (int k = 32; k>=0 && !path_found; --k) {
63774           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k);
63775           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63776         }
63777         for (int k = 32; k>=10 && !path_found; --k) {
63778           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
63779           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63780         }
63781         for (int k = 9; k>=0 && !path_found; --k) {
63782           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
63783           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63784         }
63785         for (int k = 32; k>=0 && !path_found; --k) {
63786           cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
63787           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63788         }
63789         if (!path_found) std::strcpy(s_path,"gm.exe");
63790 #else
63791         if (!path_found) {
63792           std::strcpy(s_path,"./gm");
63793           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63794         }
63795         if (!path_found) std::strcpy(s_path,"gm");
63796 #endif
63797         winformat_string(s_path);
63798       }
63799       cimg::mutex(7,0);
63800       return s_path;
63801     }
63802 
63803     //! Get/set path to the \c gunzip binary.
63804     /**
63805        \param user_path Specified path, or \c 0 to get the path currently used.
63806        \param reinit_path Force path to be recalculated (may take some time).
63807        \return Path containing the \c gunzip binary.
63808     **/
63809     inline const char *gunzip_path(const char *const user_path, const bool reinit_path) {
63810       static CImg<char> s_path;
63811       cimg::mutex(7);
63812       if (reinit_path) s_path.assign();
63813       if (user_path) {
63814         if (!s_path) s_path.assign(1024);
63815         std::strncpy(s_path,user_path,1023);
63816       } else if (!s_path) {
63817         s_path.assign(1024);
63818         bool path_found = false;
63819         std::FILE *file = 0;
63820 #if cimg_OS==2
63821         if (win_searchpath("gunzip.exe",s_path,s_path._width)) path_found = true;
63822         if (!path_found) {
63823           std::strcpy(s_path,".\\gunzip.exe");
63824           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63825         }
63826         if (!path_found) std::strcpy(s_path,"gunzip.exe");
63827 #else
63828         if (!path_found) {
63829           std::strcpy(s_path,"./gunzip");
63830           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63831         }
63832         if (!path_found) std::strcpy(s_path,"gunzip");
63833 #endif
63834         winformat_string(s_path);
63835       }
63836       cimg::mutex(7,0);
63837       return s_path;
63838     }
63839 
63840     //! Get/set path to the \c gzip binary.
63841     /**
63842        \param user_path Specified path, or \c 0 to get the path currently used.
63843        \param reinit_path Force path to be recalculated (may take some time).
63844        \return Path containing the \c gzip binary.
63845     **/
63846     inline const char *gzip_path(const char *const user_path, const bool reinit_path) {
63847       static CImg<char> s_path;
63848       cimg::mutex(7);
63849       if (reinit_path) s_path.assign();
63850       if (user_path) {
63851         if (!s_path) s_path.assign(1024);
63852         std::strncpy(s_path,user_path,1023);
63853       } else if (!s_path) {
63854         s_path.assign(1024);
63855         bool path_found = false;
63856         std::FILE *file = 0;
63857 #if cimg_OS==2
63858         if (win_searchpath("gzip.exe",s_path,s_path._width)) path_found = true;
63859         if (!path_found) {
63860           std::strcpy(s_path,".\\gzip.exe");
63861           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63862         }
63863         if (!path_found) std::strcpy(s_path,"gzip.exe");
63864 #else
63865         if (!path_found) {
63866           std::strcpy(s_path,"./gzip");
63867           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63868         }
63869         if (!path_found) std::strcpy(s_path,"gzip");
63870 #endif
63871         winformat_string(s_path);
63872       }
63873       cimg::mutex(7,0);
63874       return s_path;
63875     }
63876 
63877     //! Get/set path to the ImageMagick's \c convert binary.
63878     /**
63879        \param user_path Specified path, or \c 0 to get the path currently used.
63880        \param reinit_path Force path to be recalculated (may take some time).
63881        \return Path containing the \c convert binary.
63882     **/
63883     inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
63884       static CImg<char> s_path;
63885       cimg::mutex(7);
63886       if (reinit_path) s_path.assign();
63887       if (user_path) {
63888         if (!s_path) s_path.assign(1024);
63889         std::strncpy(s_path,user_path,1023);
63890       } else if (!s_path) {
63891         s_path.assign(1024);
63892         bool path_found = false;
63893         std::FILE *file = 0;
63894 #if cimg_OS==2
63895         if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true;
63896         const char *const pf_path = win_programfiles_path();
63897         for (int l = 0; l<2 && !path_found; ++l) {
63898           const char *const s_exe = l?"convert":"magick";
63899           cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
63900           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63901           for (int k = 32; k>=10 && !path_found; --k) {
63902             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
63903             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63904           }
63905           for (int k = 9; k>=0 && !path_found; --k) {
63906             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
63907             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63908           }
63909           for (int k = 32; k>=0 && !path_found; --k) {
63910             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
63911             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63912           }
63913           for (int k = 32; k>=10 && !path_found; --k) {
63914             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
63915             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63916           }
63917           for (int k = 9; k>=0 && !path_found; --k) {
63918             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
63919             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63920           }
63921           for (int k = 32; k>=0 && !path_found; --k) {
63922             cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
63923             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63924           }
63925           for (int k = 32; k>=10 && !path_found; --k) {
63926             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
63927             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63928           }
63929           for (int k = 9; k>=0 && !path_found; --k) {
63930             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
63931             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63932           }
63933           for (int k = 32; k>=0 && !path_found; --k) {
63934             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
63935             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63936           }
63937           for (int k = 32; k>=10 && !path_found; --k) {
63938             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
63939             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63940           }
63941           for (int k = 9; k>=0 && !path_found; --k) {
63942             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
63943             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63944           }
63945           for (int k = 32; k>=0 && !path_found; --k) {
63946             cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
63947             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63948           }
63949           for (int k = 32; k>=10 && !path_found; --k) {
63950             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
63951             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63952           }
63953           for (int k = 9; k>=0 && !path_found; --k) {
63954             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
63955             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63956           }
63957           for (int k = 32; k>=0 && !path_found; --k) {
63958             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
63959             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63960           }
63961           for (int k = 32; k>=10 && !path_found; --k) {
63962             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
63963             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63964           }
63965           for (int k = 9; k>=0 && !path_found; --k) {
63966             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
63967             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63968           }
63969           for (int k = 32; k>=0 && !path_found; --k) {
63970             cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
63971             if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63972           }
63973           if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
63974         }
63975 #else
63976         std::strcpy(s_path,"./magick");
63977         if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63978         if (!path_found) {
63979           std::strcpy(s_path,"./convert");
63980           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
63981         }
63982         if (!path_found) std::strcpy(s_path,"convert");
63983 #endif
63984         winformat_string(s_path);
63985       }
63986       cimg::mutex(7,0);
63987       return s_path;
63988     }
63989 
63990     //! Get/set path to the Medcon's \c medcon binary.
63991     /**
63992        \param user_path Specified path, or \c 0 to get the path currently used.
63993        \param reinit_path Force path to be recalculated (may take some time).
63994        \return Path containing the \c medcon binary.
63995     **/
63996     inline const char* medcon_path(const char *const user_path, const bool reinit_path) {
63997       static CImg<char> s_path;
63998       cimg::mutex(7);
63999       if (reinit_path) s_path.assign();
64000       if (user_path) {
64001         if (!s_path) s_path.assign(1024);
64002         std::strncpy(s_path,user_path,1023);
64003       } else if (!s_path) {
64004         s_path.assign(1024);
64005         bool path_found = false;
64006         std::FILE *file = 0;
64007 #if cimg_OS==2
64008         if (win_searchpath("medcon.exe",s_path,s_path._width)) path_found = true;
64009         const char *const pf_path = win_programfiles_path();
64010         if (!path_found) {
64011           std::strcpy(s_path,".\\medcon.exe");
64012           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64013         }
64014         if (!path_found) {
64015           cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
64016           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64017         }
64018         if (!path_found) {
64019           cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
64020           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64021         }
64022         if (!path_found) {
64023           std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
64024           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64025         }
64026         if (!path_found) std::strcpy(s_path,"medcon.exe");
64027 #else
64028         if (!path_found) {
64029           std::strcpy(s_path,"./medcon");
64030           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64031         }
64032         if (!path_found) std::strcpy(s_path,"medcon");
64033 #endif
64034         winformat_string(s_path);
64035       }
64036       cimg::mutex(7,0);
64037       return s_path;
64038     }
64039 
64040     //! Get/set path to store temporary files.
64041     /**
64042        \param user_path Specified path, or \c 0 to get the path currently used.
64043        \param reinit_path Force path to be recalculated (may take some time).
64044        \return Path where temporary files can be saved.
64045     **/
64046     inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
64047 #define _cimg_test_temporary_path(p) \
64048       if (!path_found) { \
64049         cimg_snprintf(s_path,s_path.width(),"%s",p); \
64050         cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
64051         if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
64052       }
64053       static CImg<char> s_path;
64054       cimg::mutex(7);
64055       if (reinit_path) s_path.assign();
64056       if (user_path) {
64057         if (!s_path) s_path.assign(1024);
64058         std::strncpy(s_path,user_path,1023);
64059       } else if (!s_path) {
64060         s_path.assign(1024);
64061         bool path_found = false;
64062         CImg<char> tmp(1024), filename_tmp(256);
64063         std::FILE *file = 0;
64064         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand());
64065         char *tmpPath = std::getenv("TMP");
64066         if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
64067         if (tmpPath) _cimg_test_temporary_path(tmpPath);
64068 #if cimg_OS==2
64069         _cimg_test_temporary_path("C:\\WINNT\\Temp");
64070         _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
64071         _cimg_test_temporary_path("C:\\Temp");
64072         _cimg_test_temporary_path("C:");
64073         _cimg_test_temporary_path("D:\\WINNT\\Temp");
64074         _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
64075         _cimg_test_temporary_path("D:\\Temp");
64076         _cimg_test_temporary_path("D:");
64077 #else
64078         _cimg_test_temporary_path("/tmp");
64079         _cimg_test_temporary_path("/var/tmp");
64080 #endif
64081         if (!path_found) {
64082           *s_path = 0;
64083           std::strncpy(tmp,filename_tmp,tmp._width - 1);
64084           if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
64085         }
64086         if (!path_found) {
64087           cimg::mutex(7,0);
64088           throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
64089         }
64090       }
64091       cimg::mutex(7,0);
64092       return s_path;
64093     }
64094 
64095     //! Get/set path to the \c wget binary.
64096     /**
64097        \param user_path Specified path, or \c 0 to get the path currently used.
64098        \param reinit_path Force path to be recalculated (may take some time).
64099        \return Path containing the \c wget binary.
64100     **/
64101     inline const char *wget_path(const char *const user_path, const bool reinit_path) {
64102       static CImg<char> s_path;
64103       cimg::mutex(7);
64104       if (reinit_path) s_path.assign();
64105       if (user_path) {
64106         if (!s_path) s_path.assign(1024);
64107         std::strncpy(s_path,user_path,1023);
64108       } else if (!s_path) {
64109         s_path.assign(1024);
64110         bool path_found = false;
64111         std::FILE *file = 0;
64112 #if cimg_OS==2
64113         if (win_searchpath("wget.exe",s_path,s_path._width)) path_found = true;
64114         if (!path_found) {
64115           std::strcpy(s_path,".\\wget.exe");
64116           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64117         }
64118         if (!path_found) std::strcpy(s_path,"wget.exe");
64119 #else
64120         if (!path_found) {
64121           std::strcpy(s_path,"./wget");
64122           if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
64123         }
64124         if (!path_found) std::strcpy(s_path,"wget");
64125 #endif
64126         winformat_string(s_path);
64127       }
64128       cimg::mutex(7,0);
64129       return s_path;
64130     }
64131 
64132 
64133     // [internal] Sorting function, used by cimg::files().
64134     inline int _sort_files(const void* a, const void* b) {
64135       const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b;
64136       return std::strcmp(sa._data,sb._data);
64137     }
64138 
64139     //! Generate a numbered version of a filename.
64140     inline char* number_filename(const char *const filename, const int number,
64141                                  const unsigned int digits, char *const str) {
64142       if (!filename) { if (str) *str = 0; return 0; }
64143       const unsigned int siz = (unsigned int)std::strlen(filename);
64144       CImg<char> format(16), body(siz + 32);
64145       const char *const ext = cimg::split_filename(filename,body);
64146       if (*ext) cimg_snprintf(format,format.width(),"%%s_%%.%ud.%%s",digits);
64147       else cimg_snprintf(format,format.width(),"%%s_%%.%ud",digits);
64148       cimg_snprintf(str,1024,format._data,body._data,number,ext);
64149       return str;
64150     }
64151 
64152     //! Return list of files/directories in specified directory.
64153     /**
64154        \param path Path to the directory. Set to 0 for current directory.
64155        \param is_pattern Tell if specified path has a matching pattern in it.
64156        \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }.
64157        \param include_path Tell if \c path must be included in resulting filenames.
64158        \return A list of filenames.
64159     **/
64160     inline CImgList<char> files(const char *const path, const bool is_pattern=false,
64161                                 const unsigned int mode=2, const bool include_path=false) {
64162       if (!path || !*path) return files("*",true,mode,include_path);
64163       CImgList<char> res;
64164 
64165       // If path is a valid folder name, ignore argument 'is_pattern'.
64166       const bool _is_pattern = is_pattern && !cimg::is_directory(path);
64167       bool is_root = false, is_current = false;
64168       cimg::unused(is_root,is_current);
64169 
64170       // Clean format of input path.
64171       CImg<char> pattern, _path = CImg<char>::string(path);
64172 #if cimg_OS==2
64173       for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/';
64174 #endif
64175       char *pd = _path;
64176       for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; }
64177       *pd = 0;
64178       unsigned int lp = (unsigned int)std::strlen(_path);
64179       if (!_is_pattern && lp && _path[lp - 1]=='/') {
64180         _path[lp - 1] = 0; --lp;
64181 #if cimg_OS!=2
64182         is_root = !*_path;
64183 #endif
64184       }
64185 
64186       // Separate folder path and matching pattern.
64187       if (_is_pattern) {
64188         const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data());
64189         CImg<char>::string(_path).move_to(pattern);
64190         if (bpos) {
64191           _path[bpos - 1] = 0; // End 'path' at last slash
64192 #if cimg_OS!=2
64193           is_root = !*_path;
64194 #endif
64195         } else { // No path to folder specified, assuming current folder
64196           is_current = true; *_path = 0;
64197         }
64198         lp = (unsigned int)std::strlen(_path);
64199       }
64200 
64201       // Windows version.
64202 #if cimg_OS==2
64203       if (!_is_pattern) {
64204         pattern.assign(lp + 3);
64205         std::memcpy(pattern,_path,lp);
64206         pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0;
64207       }
64208       WIN32_FIND_DATAA file_data;
64209       const HANDLE dir = FindFirstFileA(pattern.data(),&file_data);
64210       if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty();
64211       do {
64212         const char *const filename = file_data.cFileName;
64213         if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
64214           const unsigned int lf = (unsigned int)std::strlen(filename);
64215           const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0;
64216           if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) {
64217             if (include_path) {
64218               CImg<char> full_filename((lp?lp+1:0) + lf + 1);
64219               if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; }
64220               std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1);
64221               full_filename.move_to(res);
64222             } else CImg<char>(filename,lf + 1).move_to(res);
64223           }
64224         }
64225       } while (FindNextFileA(dir,&file_data));
64226       FindClose(dir);
64227 
64228       // Unix version (posix).
64229 #elif cimg_OS == 1
64230       DIR *const dir = opendir(is_root?"/":is_current?".":_path.data());
64231       if (!dir) return CImgList<char>::const_empty();
64232       struct dirent *ent;
64233       while ((ent=readdir(dir))!=0) {
64234         const char *const filename = ent->d_name;
64235         if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
64236           const unsigned int lf = (unsigned int)std::strlen(filename);
64237           CImg<char> full_filename(lp + lf + 2);
64238 
64239           if (!is_current) {
64240             full_filename.assign(lp + lf + 2);
64241             if (lp) std::memcpy(full_filename,_path,lp);
64242             full_filename[lp] = '/';
64243             std::memcpy(full_filename._data + lp + 1,filename,lf + 1);
64244           } else full_filename.assign(filename,lf + 1);
64245 
64246           struct stat st;
64247           if (stat(full_filename,&st)==-1) continue;
64248           const bool is_directory = (st.st_mode & S_IFDIR)!=0;
64249           if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) {
64250             if (include_path) {
64251               if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
64252                 full_filename.move_to(res);
64253             } else {
64254               if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
64255                 CImg<char>(filename,lf + 1).move_to(res);
64256             }
64257           }
64258         }
64259       }
64260       closedir(dir);
64261 #endif
64262 
64263       // Sort resulting list by lexicographic order.
64264       if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files);
64265 
64266       return res;
64267     }
64268 
64269     //! Try to guess format from an image file.
64270     /**
64271        \param file Input file (can be \c 0 if \c filename is set).
64272        \param filename Filename, as a C-string (can be \c 0 if \c file is set).
64273        \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
64274     **/
64275     inline const char *ftype(std::FILE *const file, const char *const filename) {
64276       if (!file && !filename)
64277         throw CImgArgumentException("cimg::ftype(): Specified filename is (null).");
64278       static const char
64279         *const _pnm = "pnm",
64280         *const _pfm = "pfm",
64281         *const _bmp = "bmp",
64282         *const _gif = "gif",
64283         *const _jpg = "jpg",
64284         *const _off = "off",
64285         *const _pan = "pan",
64286         *const _png = "png",
64287         *const _tif = "tif",
64288         *const _inr = "inr",
64289         *const _dcm = "dcm";
64290       const char *f_type = 0;
64291       CImg<char> header;
64292       const unsigned int omode = cimg::exception_mode();
64293       cimg::exception_mode(0);
64294       try {
64295         header._load_raw(file,filename,512,1,1,1,false,false,0);
64296         const unsigned char *const uheader = (unsigned char*)header._data;
64297         if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF
64298         else if (!std::strncmp(header,"#INRIMAGE",9)) // INRIMAGE
64299           f_type = _inr;
64300         else if (!std::strncmp(header,"PANDORE",7)) // PANDORE
64301           f_type = _pan;
64302         else if (!std::strncmp(header.data() + 128,"DICM",4)) // DICOM
64303           f_type = _dcm;
64304         else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) // JPEG
64305           f_type = _jpg;
64306         else if (header[0]=='B' && header[1]=='M') // BMP
64307           f_type = _bmp;
64308         else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' &&
64309                  (header[4]=='7' || header[4]=='9')) // GIF
64310           f_type = _gif;
64311         else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&
64312                  uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG
64313           f_type = _png;
64314         else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) // TIFF
64315           f_type = _tif;
64316         else { // PNM or PFM
64317           CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false);
64318           cimglist_for(_header,l) {
64319             if (_header(l,0)=='#') continue;
64320             if (_header[l]._width==2 && _header(l,0)=='P') {
64321               const char c = _header(l,1);
64322               if (c=='f' || c=='F') { f_type = _pfm; break; }
64323               if (c>='1' && c<='9') { f_type = _pnm; break; }
64324             }
64325             f_type = 0; break;
64326           }
64327         }
64328       } catch (CImgIOException&) { }
64329       cimg::exception_mode(omode);
64330       return f_type;
64331     }
64332 
64333     //! Load file from network as a local temporary file.
64334     /**
64335        \param url URL of the filename, as a C-string.
64336        \param[out] filename_local C-string containing the path to a local copy of \c filename.
64337        \param timeout Maximum time (in seconds) authorized for downloading the file from the URL.
64338        \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure.
64339        \param referer Referer used, as a C-string.
64340        \return Value of \c filename_local.
64341        \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download.
64342     **/
64343     inline char *load_network(const char *const url, char *const filename_local,
64344                               const unsigned int timeout, const bool try_fallback,
64345                               const char *const referer) {
64346       if (!url)
64347         throw CImgArgumentException("cimg::load_network(): Specified URL is (null).");
64348       if (!filename_local)
64349         throw CImgArgumentException("cimg::load_network(): Specified destination string is (null).");
64350       if (!network_mode())
64351         throw CImgIOException("cimg::load_network(): Loading files from network is disabled.");
64352 
64353       const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext;
64354       CImg<char> ext = CImg<char>::string(_ext);
64355       std::FILE *file = 0;
64356       *filename_local = 0;
64357       if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0;
64358       else cimg::strwindows_reserved(ext);
64359       do {
64360         cimg_snprintf(filename_local,256,"%s%c%s%s",
64361                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data);
64362         if ((file=cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file);
64363       } while (file);
64364 
64365 #ifdef cimg_use_curl
64366       const unsigned int omode = cimg::exception_mode();
64367       cimg::exception_mode(0);
64368       try {
64369         CURL *curl = 0;
64370         CURLcode res;
64371         curl = curl_easy_init();
64372         if (curl) {
64373           file = cimg::fopen(filename_local,"wb");
64374           curl_easy_setopt(curl,CURLOPT_URL,url);
64375           curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0);
64376           curl_easy_setopt(curl,CURLOPT_WRITEDATA,file);
64377           curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
64378           curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L);
64379           curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
64380           if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout);
64381           if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
64382           if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer);
64383           res = curl_easy_perform(curl);
64384           curl_easy_cleanup(curl);
64385           cimg::fseek(file,0,SEEK_END); // Check if file size is 0
64386           const cimg_ulong siz = cimg::ftell(file);
64387           cimg::fclose(file);
64388           if (siz>0 && res==CURLE_OK) {
64389             cimg::exception_mode(omode);
64390             return filename_local;
64391           } else std::remove(filename_local);
64392         }
64393       } catch (...) { }
64394       cimg::exception_mode(omode);
64395       if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url);
64396 #endif
64397 
64398       CImg<char> command((unsigned int)std::strlen(url) + 64);
64399       cimg::unused(try_fallback);
64400 
64401       // Try with 'curl' first.
64402       if (timeout) {
64403         if (referer)
64404           cimg_snprintf(command,command._width,"\"%s\" -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
64405                         cimg::curl_path(),referer,timeout,filename_local,
64406                         CImg<char>::string(url)._system_strescape().data());
64407         else
64408           cimg_snprintf(command,command._width,"\"%s\" -m %u -f --silent --compressed -o \"%s\" \"%s\"",
64409                         cimg::curl_path(),timeout,filename_local,
64410                         CImg<char>::string(url)._system_strescape().data());
64411       } else {
64412         if (referer)
64413           cimg_snprintf(command,command._width,"\"%s\" -e %s -f --silent --compressed -o \"%s\" \"%s\"",
64414                         cimg::curl_path(),referer,filename_local,
64415                         CImg<char>::string(url)._system_strescape().data());
64416         else
64417           cimg_snprintf(command,command._width,"\"%s\" -f --silent --compressed -o \"%s\" \"%s\"",
64418                         cimg::curl_path(),filename_local,
64419                         CImg<char>::string(url)._system_strescape().data());
64420       }
64421       cimg::system(command, cimg::curl_path());
64422 
64423       if (!(file=cimg::std_fopen(filename_local,"rb"))) {
64424 
64425         // Try with 'wget' otherwise.
64426         if (timeout) {
64427           if (referer)
64428             cimg_snprintf(command,command._width,"\"%s\" --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
64429                           cimg::wget_path(),referer,timeout,filename_local,
64430                           CImg<char>::string(url)._system_strescape().data());
64431           else
64432             cimg_snprintf(command,command._width,"\"%s\" -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
64433                           cimg::wget_path(),timeout,filename_local,
64434                           CImg<char>::string(url)._system_strescape().data());
64435         } else {
64436           if (referer)
64437             cimg_snprintf(command,command._width,"\"%s\" --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
64438                           cimg::wget_path(),referer,filename_local,
64439                           CImg<char>::string(url)._system_strescape().data());
64440           else
64441             cimg_snprintf(command,command._width,"\"%s\" -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
64442                           cimg::wget_path(),filename_local,
64443                           CImg<char>::string(url)._system_strescape().data());
64444         }
64445         cimg::system(command, cimg::wget_path());
64446 
64447         if (!(file=cimg::std_fopen(filename_local,"rb")))
64448           throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands "
64449                                 "'wget' or 'curl'.",url);
64450         cimg::fclose(file);
64451 
64452         // Try gunzip it.
64453         cimg_snprintf(command,command._width,"%s.gz",filename_local);
64454         std::rename(filename_local,command);
64455         cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"",
64456                       gunzip_path(),filename_local);
64457         cimg::system(command, gunzip_path());
64458         file = cimg::std_fopen(filename_local,"rb");
64459         if (!file) {
64460           cimg_snprintf(command,command._width,"%s.gz",filename_local);
64461           std::rename(command,filename_local);
64462           file = cimg::std_fopen(filename_local,"rb");
64463         }
64464       }
64465       cimg::fseek(file,0,SEEK_END); // Check if file size is 0
64466       if (std::ftell(file)<=0)
64467         throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands "
64468                               "'wget' or 'curl'.",url);
64469       cimg::fclose(file);
64470       return filename_local;
64471     }
64472 
64473     // Implement a tic/toc mechanism to display elapsed time of algorithms.
64474     inline cimg_uint64 tictoc(const bool is_tic) {
64475       cimg::mutex(2);
64476       static CImg<cimg_uint64> times(64);
64477       static unsigned int pos = 0;
64478       const cimg_uint64 t1 = cimg::time();
64479       if (is_tic) {
64480         // Tic
64481         times[pos++] = t1;
64482         if (pos>=times._width)
64483           throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
64484         cimg::mutex(2,0);
64485         return t1;
64486       }
64487 
64488       // Toc
64489       if (!pos)
64490         throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
64491       const cimg_uint64
64492         t0 = times[--pos],
64493         dt = t1>=t0?(t1 - t0):cimg::type<cimg_uint64>::max();
64494       const unsigned int
64495         edays = (unsigned int)(dt/86400000.),
64496         ehours = (unsigned int)((dt - edays*86400000.)/3600000.),
64497         emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.),
64498         esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.),
64499         ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.);
64500       if (!edays && !ehours && !emin && !esec)
64501         std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
64502                      cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
64503       else {
64504         if (!edays && !ehours && !emin)
64505           std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
64506                        cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
64507         else {
64508           if (!edays && !ehours)
64509             std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
64510                          cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
64511           else{
64512             if (!edays)
64513               std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
64514                            cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
64515             else{
64516               std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
64517                            cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
64518             }
64519           }
64520         }
64521       }
64522       cimg::mutex(2,0);
64523       return dt;
64524     }
64525 
64526     // Return a temporary string describing the size of a memory buffer.
64527     inline const char *strbuffersize(const cimg_ulong size) {
64528       static CImg<char> res(256);
64529       cimg::mutex(5);
64530       if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":"");
64531       else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); }
64532       else if (size<1024*1024*1024LU) {
64533         const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize);
64534       } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); }
64535       cimg::mutex(5,0);
64536       return res;
64537     }
64538 
64539     //! Display a simple dialog box, and wait for the user's response.
64540     /**
64541        \param title Title of the dialog window.
64542        \param msg Main message displayed inside the dialog window.
64543        \param button1_label Label of the 1st button.
64544        \param button2_label Label of the 2nd button (\c 0 to hide button).
64545        \param button3_label Label of the 3rd button (\c 0 to hide button).
64546        \param button4_label Label of the 4th button (\c 0 to hide button).
64547        \param button5_label Label of the 5th button (\c 0 to hide button).
64548        \param button6_label Label of the 6th button (\c 0 to hide button).
64549        \param logo Image logo displayed at the left of the main message.
64550        \param is_centered Tells if the dialog window must be centered on the screen.
64551        \return Index of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
64552        \note
64553        - Up to 6 buttons can be defined in the dialog window.
64554        - The function returns when a user clicked one of the button or closed the dialog window.
64555        - If a button text is set to 0, the corresponding button (and the following) will not appear in the dialog box.
64556        At least one button must be specified.
64557     **/
64558     template<typename t>
64559     inline int dialog(const char *const title, const char *const msg,
64560                       const char *const button1_label, const char *const button2_label,
64561                       const char *const button3_label, const char *const button4_label,
64562                       const char *const button5_label, const char *const button6_label,
64563                       const CImg<t>& logo, const bool is_centered=false) {
64564 #if cimg_display==0
64565       cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
64566                    logo._data,is_centered);
64567       throw CImgIOException("cimg::dialog(): No display available.");
64568 #else
64569       static const unsigned char
64570         black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
64571 
64572       // Create buttons and canvas graphics
64573       CImgList<unsigned char> buttons, cbuttons, sbuttons;
64574       if (button1_label) {
64575         CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
64576         if (button2_label) {
64577           CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
64578           if (button3_label) {
64579             CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
64580             if (button4_label) {
64581               CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
64582               if (button5_label) {
64583                 CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
64584                 if (button6_label) {
64585                   CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
64586                 }}}}}}
64587       if (!buttons._width)
64588         throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
64589       cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
64590 
64591       unsigned int bw = 0, bh = 0;
64592       cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); }
64593       bw+=8; bh+=8;
64594       if (bw<64) bw = 64;
64595       if (bw>128) bw = 128;
64596       if (bh<24) bh = 24;
64597       if (bh>48) bh = 48;
64598 
64599       CImg<unsigned char> button(bw,bh,1,3);
64600       button.draw_rectangle(0,0,bw - 1,bh - 1,gray);
64601       button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white);
64602       button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black);
64603       button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2);
64604       CImg<unsigned char> sbutton(bw,bh,1,3);
64605       sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray);
64606       sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black);
64607       sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black);
64608       sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white);
64609       sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black);
64610       sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2);
64611       sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
64612         draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
64613       sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
64614         draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
64615       CImg<unsigned char> cbutton(bw,bh,1,3);
64616       cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2).
64617         draw_rectangle(2,2,bw - 3,bh - 3,gray);
64618       cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
64619         draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
64620       cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
64621         draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
64622 
64623       cimglist_for(buttons,ll) {
64624         CImg<unsigned char>(cbutton).
64625           draw_image(1 + (bw  -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]).
64626           move_to(cbuttons);
64627         CImg<unsigned char>(sbutton).
64628           draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
64629           move_to(sbuttons);
64630         CImg<unsigned char>(button).
64631           draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
64632           move_to(buttons[ll]);
64633       }
64634 
64635       CImg<unsigned char> canvas;
64636       if (msg)
64637         ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
64638 
64639       const unsigned int
64640         bwall = (buttons._width - 1)*(12 + bw) + bw,
64641         w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall),
64642         h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh),
64643         lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)),
64644         ly = (h - 12 - bh - logo._height)/2,
64645         tx = lx + logo._width + 12,
64646         ty = (h - 12 - bh - canvas._height)/2,
64647         bx = (w - bwall)/2,
64648         by = h - 12 - bh;
64649 
64650       if (canvas._data)
64651         canvas = CImg<unsigned char>(w,h,1,3).
64652           draw_rectangle(0,0,w - 1,h - 1,gray).
64653           draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
64654           draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black).
64655           draw_image(tx,ty,canvas);
64656       else
64657         canvas = CImg<unsigned char>(w,h,1,3).
64658           draw_rectangle(0,0,w - 1,h - 1,gray).
64659           draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
64660           draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black);
64661       if (logo._data) canvas.draw_image(lx,ly,logo);
64662 
64663       unsigned int xbuttons[6] = { 0 };
64664       cimglist_for(buttons,lll) {
64665         xbuttons[lll] = bx + (bw + 12)*lll;
64666         canvas.draw_image(xbuttons[lll],by,buttons[lll]);
64667       }
64668 
64669       // Open window and enter events loop
64670       CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
64671       if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
64672                                  (CImgDisplay::screen_height() - disp.height())/2);
64673       bool stop_flag = false, refresh = false;
64674       int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
64675       while (!disp.is_closed() && !stop_flag) {
64676         if (refresh) {
64677           if (clicked>=0)
64678             CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
64679           else {
64680             if (selected>=0)
64681               CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
64682             else canvas.display(disp);
64683           }
64684           refresh = false;
64685         }
64686         disp.wait(15);
64687         if (disp.is_resized()) disp.resize(disp,false);
64688 
64689         if (disp.button()&1)  {
64690           oclicked = clicked;
64691           clicked = -1;
64692           cimglist_for(buttons,l)
64693             if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) &&
64694                 disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) {
64695               clicked = selected = l;
64696               refresh = true;
64697             }
64698           if (clicked!=oclicked) refresh = true;
64699         } else if (clicked>=0) stop_flag = true;
64700 
64701         if (disp.key()) {
64702           oselected = selected;
64703           switch (disp.key()) {
64704           case cimg::keyESC : selected = -1; stop_flag = true; break;
64705           case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
64706           case cimg::keyTAB :
64707           case cimg::keyARROWRIGHT :
64708           case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break;
64709           case cimg::keyARROWLEFT :
64710           case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break;
64711           }
64712           disp.set_key();
64713           if (selected!=oselected) refresh = true;
64714         }
64715       }
64716       if (!disp) selected = -1;
64717       return selected;
64718 #endif
64719     }
64720 
64721     //! Display a simple dialog box, and wait for the user's response \specialization.
64722     inline int dialog(const char *const title, const char *const msg,
64723                       const char *const button1_label, const char *const button2_label,
64724                       const char *const button3_label, const char *const button4_label,
64725                       const char *const button5_label, const char *const button6_label,
64726                       const bool is_centered) {
64727       return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
64728                     CImg<unsigned char>::_logo40x38(),is_centered);
64729     }
64730 
64731     //! Evaluate math expression.
64732     /**
64733        \param expression C-string describing the formula to evaluate.
64734        \param x Value of the pre-defined variable \c x.
64735        \param y Value of the pre-defined variable \c y.
64736        \param z Value of the pre-defined variable \c z.
64737        \param c Value of the pre-defined variable \c c.
64738        \return Result of the formula evaluation.
64739        \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
64740        \par Example
64741        \code
64742        const double
64743        res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2),  // will return '1'
64744        res2 = cimg::eval(0,1,1);                    // will return '1' too
64745        \endcode
64746     **/
64747     inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
64748       static const CImg<float> empty;
64749       return empty.eval(expression,x,y,z,c);
64750     }
64751 
64752     template<typename t>
64753     inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
64754       static const CImg<float> empty;
64755       return empty.eval(expression,xyzc);
64756     }
64757 
64758   } // namespace cimg { ...
64759 } // namespace cimg_library { ...
64760 
64761 //! Short alias name.
64762 namespace cil = cimg_library_suffixed;
64763 
64764 #ifdef _cimg_redefine_False
64765 #define False 0
64766 #endif
64767 #ifdef _cimg_redefine_True
64768 #define True 1
64769 #endif
64770 #ifdef _cimg_redefine_Status
64771 #define Status int
64772 #endif
64773 #ifdef _cimg_redefine_Success
64774 #define Success 0
64775 #endif
64776 #ifdef _cimg_redefine_min
64777 #define min(a,b) (((a)<(b))?(a):(b))
64778 #endif
64779 #ifdef _cimg_redefine_max
64780 #define max(a,b) (((a)>(b))?(a):(b))
64781 #endif
64782 #ifdef _cimg_redefine_PI
64783 #define PI 3.141592653589793238462643383
64784 #endif
64785 #ifdef _MSC_VER
64786 #pragma warning(pop)
64787 #endif
64788 
64789 #endif
64790 
64791 // Local Variables:
64792 // mode: c++
64793 // End:
64794